Files
mars-nwe/src/nwconn.c
Mario Fetka 03a5d69dc4
All checks were successful
Source release / source-package (push) Successful in 48s
nwconn: implement AFP Get File Information
Wire NCP 0x23/0x05 AFP Get File Information to a conservative read-only reply
for SYS:-style paths.

The WebSDK documents NCP 0x2222/35/05 as taking a Volume Number, AFP Entry ID,
request bit map, and AFP path modifier string, and returning an AFP file
information record with entry id, parent id, attributes, data and resource fork
lengths, offspring count, NetWare dates, Finder Info, long and short names,
owner id, access privileges, and ProDOS information. The SDK headers expose the
same call as AFPGetFileInformation() and NWAFPGetFileInformation(), with the
wire reply matching RECPKT_AFPFILEINFO.

Resolve the supplied path through the existing mars_nwe path machinery, require
the optional Netatalk/libatalk backend as for the entry-id probe, and fill the
fields that can be derived safely from Unix stat data and the existing libatalk
helpers. Finder Info and resource fork length are read through nwatalk when
present; entry ids fall back to the existing stat-derived AFP id until
persistent CNID/AppleDouble ids are implemented. Parent id and ProDOS-specific
data remain zero for now.

Add a Linux afp_file_info_smoke test using ncpfs/libncp so the new call can be
exercised without an AppleTalk client. The test sends raw SYS:-style paths with
directory handle 0, matching the verified AFP Entry ID smoke-test path.

This implements only the read-only AFP file information query for path-based
requests; entry-id-only lookup, persistent CNID mapping, and write-side AFP
semantics remain future work.
2026-05-30 03:59:50 +02:00

4013 lines
163 KiB
C
Raw Blame History

/* nwconn.c 18-Apr-00 */
/* one process / connection */
/* (C)opyright (C) 1993,2000 Martin Stover, Marburg, Germany
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* history since 21-Apr-00
*
* mst:25-Apr-00: added routines for sending nwconn data to nwbind
*
*/
#include "net.h"
#if 1
# define LOC_RW_BUFFERSIZE RW_BUFFERSIZE
#else
# define LOC_RW_BUFFERSIZE 512
#endif
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#if !CALL_NWCONN_OVER_SOCKET
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include "nwvolume.h"
#include "nwfile.h"
#include "connect.h"
#include "nwqconn.h"
#include "namspace.h"
#include "nwshare.h"
#include "nwconn.h"
#include "trustee.h"
#include "nwatalk.h"
int act_pid = 0;
#define FD_NCP_OUT 3
static int father_pid = -1;
static ipxAddr_t from_addr;
static ipxAddr_t my_addr;
static struct t_unitdata ud;
static uint8 ipx_pack_typ = PACKT_CORE;
static int last_sequence = -9999;
static IPX_DATA ipxdata;
static NCPRESPONSE *ncpresponse=(NCPRESPONSE*)&ipxdata;
static uint8 *responsedata=((uint8*)&ipxdata)+sizeof(NCPRESPONSE);
static int requestlen;
static uint8 readbuff[IPX_MAX_DATA];
static uint8 saved_readbuff[IPX_MAX_DATA];
static int saved_sequence=-1;
static int rw_buffer_size = LOC_RW_BUFFERSIZE; /* default */
static NCPREQUEST *ncprequest = (NCPREQUEST*)readbuff;
static uint8 *requestdata = readbuff + sizeof(NCPREQUEST);
static int ncp_type;
static int sock_nwbind=-1;
static int sock_echo =-1;
static char *prog_title;
static int req_printed=0;
static int scan_volume_user_disk_restrictions(uint8 volnr, uint32 sequence,
uint8 *response)
{
struct XDATA {
uint8 entries;
} *xdata = (struct XDATA *)response;
int result = nw_get_volume_name(volnr, NULL, 0);
(void)sequence;
if (result < 0)
return result;
/*
* Keep the scan reply empty for now. The per-object quota query can be
* routed through nwbind because it has a concrete object id to resolve,
* but this scan call would need bindery iteration plus UNIX_USER property
* lookups from nwbind-side code. nwconn does not link the bindery database
* helpers, and pulling them into this process would break the existing
* process split.
*/
xdata->entries = 0;
return 1;
}
static int trustee_v3_to_ncp22_rights(int rights)
{
int ncp22 = 0;
/*
* NetWare trustee/access masks use bit 0x0100 for Supervisor.
* Bit 0x0004 is the old Open right, not Supervisor.
*
* MARS keeps the same bit positions internally, including TRUSTEE_O
* at 0x0004. Do not translate TRUSTEE_S down to 0x0004 here; doing
* so makes clients see Open instead of Supervisor and RIGHTS prints
* [ RWCEMFA] instead of [SRWCEMFA].
*/
if (rights & TRUSTEE_R) ncp22 |= 0x0001; /* Read */
if (rights & TRUSTEE_W) ncp22 |= 0x0002; /* Write */
if (rights & TRUSTEE_O) ncp22 |= 0x0004; /* Open */
if (rights & TRUSTEE_C) ncp22 |= 0x0008; /* Create */
if (rights & TRUSTEE_E) ncp22 |= 0x0010; /* Erase */
if (rights & TRUSTEE_A) ncp22 |= 0x0020; /* Access Control */
if (rights & TRUSTEE_F) ncp22 |= 0x0040; /* File Scan */
if (rights & TRUSTEE_M) ncp22 |= 0x0080; /* Modify */
if (rights & TRUSTEE_S) ncp22 |= 0x0100; /* Supervisor */
return(ncp22);
}
static int trustee_ncp22_to_v3_rights(int ncp22)
{
int rights = 0;
if (ncp22 & 0x0001) rights |= TRUSTEE_R; /* Read */
if (ncp22 & 0x0002) rights |= TRUSTEE_W; /* Write */
if (ncp22 & 0x0004) rights |= TRUSTEE_O; /* Open */
if (ncp22 & 0x0008) rights |= TRUSTEE_C; /* Create */
if (ncp22 & 0x0010) rights |= TRUSTEE_E; /* Erase */
if (ncp22 & 0x0020) rights |= TRUSTEE_A; /* Access Control */
if (ncp22 & 0x0040) rights |= TRUSTEE_F; /* File Scan */
if (ncp22 & 0x0080) rights |= TRUSTEE_M; /* Modify */
if (ncp22 & 0x0100) rights |= TRUSTEE_S; /* Supervisor */
return(rights);
}
static void ncp23_debug_dump(char *what, uint8 *data, int len)
{
char hex[3 * 64 + 1];
char asc[65];
int i;
int n = len;
char *hp = hex;
if (n < 0) n = 0;
if (n > 64) n = 64;
for (i = 0; i < n; i++) {
sprintf(hp, "%02x ", (int)data[i]);
hp += 3;
asc[i] = (data[i] >= 32 && data[i] < 127) ? (char)data[i] : '.';
}
*hp = '\0';
asc[n] = '\0';
XDPRINTF((5,0,
"%s: payload_len=%d dump_len=%d hex=`%s` ascii=`%s`",
what, len, n, hex, asc));
}
static int ncp22_component_path_to_dos(uint8 *src, int count,
uint8 *dst, int maxlen,
int *used_out)
{
int used = 0;
int out = 0;
int i;
if (used_out)
*used_out = 0;
if (!dst || maxlen < 1 || count < 0)
return(-0x9c);
for (i = 0; i < count; i++) {
int len = (int)src[used++];
if (len < 0 || len > 255)
return(-0x9c);
if (out + len + ((i > 0) ? 1 : 0) >= maxlen)
return(-0x9c);
if (i > 0)
dst[out++] = '\\';
memcpy(dst + out, src + used, len);
out += len;
used += len;
}
dst[out] = '\0';
if (used_out)
*used_out = used;
return(out);
}
#if !CALL_NWCONN_OVER_SOCKET
static char* nwconn_state; /* shared memory segment will be
* attached to this pointer */
#endif
#if ENABLE_BURSTMODE
typedef struct {
BURSTPACKET *sendburst; /* buffer for sending burstpacket
* allocated and prefilled by response to
* func 0x65
* max. max_packet_size
*/
uint8 *send_buf; /* we look from servers side
* complete data buf of burst reply
* file read status + file read buf
* sendbuff must be 8 byte more allocated
* than max_send_size !
*/
int max_send_size; /* send_buf size, complete Burst DATA size */
uint32 packet_sequence; /* -> packet_sequence
* will be increased after every
* packet
*/
int burst_sequence;
struct t_unitdata ud;
ipxAddr_t to_addr;
uint8 *recv_buf; /* complete data buf for burst read requests
* must be 24 byte more allocated
* than max_recv_size !
*/
int max_recv_size; /* allocated size of recv_buf */
int max_burst_data_size; /* size of BURSTDATA, max. IPX_DATA - BURSTHEADER */
uint8 ipx_pack_typ;
} BURST_W;
static BURST_W *burst_w=NULL;
#endif
void nwconn_set_program_title(char *s)
{
memset(prog_title, 0, 49);
if (s&&*s)
strmaxcpy(prog_title, s, 48);
else
strcpy(prog_title, "()");
}
static int ncp_response(int sequence, int task,
int completition, int data_len)
{
ncpresponse->sequence = (uint8) sequence;
ncpresponse->task = (uint8) task;
ncpresponse->completition = (uint8) completition;
last_sequence = sequence;
if (req_printed) {
XDPRINTF((0,0, "NWCONN NCP_RESP seq:%d, conn:%d, compl=0x%x task=%d TO %s",
(int)ncpresponse->sequence,
(int)ncpresponse->connection
| (((int)ncpresponse->high_connection) << 8),
(int)completition,
(int)ncpresponse->task, visable_ipx_adr((ipxAddr_t *) ud.addr.buf)));
}
ud.udata.len = ud.udata.maxlen = sizeof(NCPRESPONSE) + data_len;
if (t_sndudata(FD_NCP_OUT, &ud) < 0){
if (nw_debug) t_error("t_sndudata in NWCONN !OK");
return(-1);
}
return(0);
}
static int call_nwbind(int mode)
/* modes 0: 'standard' call
* 1: activate wdog
*/
{
ipxAddr_t to_addr;
int result;
memcpy(&to_addr, &my_addr, sizeof(ipxAddr_t));
U16_TO_BE16(sock_nwbind, to_addr.sock);
ud.addr.buf = (char*)&to_addr;
if (mode==1) { /* reset wdogs */
NCPREQUEST buf;
buf.type[0] = buf.type[1]=0x22;
buf.sequence = ncprequest->sequence;
buf.connection = ncprequest->connection;
buf.task = ncprequest->task;
buf.high_connection = ncprequest->high_connection;
buf.function = 0;
ud.udata.len = ud.udata.maxlen = sizeof(buf);
ud.udata.buf = (char*)&buf;
XDPRINTF((3, 0, "send wdog reset"));
result=t_sndudata(FD_NCP_OUT, &ud);
} else {
ud.udata.len = ud.udata.maxlen = sizeof(NCPREQUEST) + requestlen;
ud.udata.buf = (char*)&readbuff;
result=t_sndudata(FD_NCP_OUT, &ud);
}
ud.addr.buf = (char*)&from_addr;
ud.udata.buf = (char*)&ipxdata;
if (result< 0){
if (nw_debug) t_error("t_sndudata in NWCONN !OK");
return(-1);
}
return(0);
}
static void pr_debug_request()
{
if (req_printed++) return;
if (ncp_type == 0x2222) {
int ufunc = 0;
switch (ncprequest->function) {
case 0x16 :
case 0x17 : ufunc = (int) *(requestdata+2); break;
case 0x57 : ufunc = (int) *(requestdata); break;
default : break;
} /* switch */
XDPRINTF((1, 0, "NCP REQUEST: func=0x%02x, ufunc=0x%02x, seq:%03d, task:%02d",
(int)ncprequest->function, ufunc,
(int)ncprequest->sequence,
(int)ncprequest->task));
} else {
XDPRINTF((1, 0, "Got NCP:type:0x%x, seq:%d, task:%d, func=0x%x",
ncp_type,
(int)ncprequest->sequence,
(int)ncprequest->task,
(int)ncprequest->function));
}
if (requestlen > 0){
int j = requestlen;
uint8 *p=requestdata;
XDPRINTF((0, 2, "len %d, DATA:", j));
while (j--) {
int c = *p++;
if (c > 32 && c < 127) XDPRINTF((0, 3,",\'%c\'", (char) c));
else XDPRINTF((0,3, ",0x%x", c));
}
XDPRINTF((0,1,NULL));
}
}
#if TEST_FNAME
static int test_handle = -1;
#endif
static void handle_nwbind_request(void) /* mst:25-Apr-00 */
{
int result = 0;
char buf[IPX_MAX_DATA];
char *data = &(buf[sizeof(NCPRESPONSE)]);
int data_len = 0;
switch (ncprequest->function) {
case 0x1: { /* get number and handles of open files */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 offset[4]; /* handle offset */
} *input = (struct INPUT *) (ncprequest);
struct XDATA {
uint8 count_handles[4];
uint8 handles[4];
} *xdata = (struct XDATA*) data;
int handles = nw_get_count_open_files(xdata->handles, GET_BE32(input->offset));
U32_TO_BE32(handles, xdata->count_handles);
data_len = (handles+1) * 4;
}
break;
default :
result = 0xfb;
}
{
NCPRESPONSE *resp = (NCPRESPONSE*)&buf;
ipxAddr_t to_addr;
memcpy(&to_addr, &my_addr, sizeof(ipxAddr_t));
U16_TO_BE16(sock_nwbind, to_addr.sock);
ud.addr.buf = (char*)&to_addr;
resp->type[0] = resp->type[1]=0x32;
resp->sequence = ncprequest->sequence;
resp->connection = ncprequest->connection;
resp->task = ncprequest->task;
resp->high_connection = ncprequest->high_connection;
resp->completition = result;
resp->connect_status = 0;
ud.udata.len = ud.udata.maxlen = sizeof(NCPRESPONSE) + data_len;
ud.udata.buf = (char*)resp;
XDPRINTF((3, 0, "send reply to nwbind"));
result = t_sndudata(FD_NCP_OUT, &ud);
ud.addr.buf = (char*)&from_addr;
ud.udata.buf = (char*)&ipxdata;
}
}
static const char *afp_call_name(int ufunc)
{
switch (ufunc) {
case 0x03: return("AFP Delete");
case 0x04: return("AFP Get Entry ID From Name");
case 0x05: return("AFP Get File Information");
case 0x06: return("AFP Get Entry ID From NetWare Handle");
case 0x07: return("AFP Rename");
case 0x08: return("AFP Open File Fork");
case 0x0b: return("AFP Alloc Temporary Dir Handle");
case 0x0c: return("AFP Get Entry ID From Path Name");
case 0x0d: return("AFP 2.0 Create Directory");
case 0x0e: return("AFP 2.0 Create File");
case 0x0f: return("AFP 2.0 Get File Information");
case 0x10: return("AFP 2.0 Set File Information");
case 0x11: return("AFP 2.0 Scan File Information");
default: return("unknown AFP call");
}
}
static int afp_request_offset(void)
/*
* AFP NCP 0x23 calls are documented with a two byte high-low request
* length followed by the AFP subfunction. Some old debug paths treated the
* first payload byte as the subfunction. Accept both layouts so diagnostics
* and the first implemented AFP call work with either requester encoding.
*/
{
if (requestlen >= 3) {
int plen = GET_BE16(requestdata);
if (plen == requestlen - 2)
return(2);
}
return(0);
}
static uint32 afp_fallback_entry_id(int volume, const struct stat *stb)
/*
* Build a stable local AFP entry id from Unix identity data when libatalk has
* no stored CNID/AppleDouble id yet. This is not a NetWare-internal directory
* base number; it is only the AFP id returned by the path-name probe.
*/
{
uint32 hash = 2166136261U;
const uint8 *p;
size_t len;
#define AFP_HASH_BYTES(ptr, size) do { \
p = (const uint8 *)(ptr); \
len = (size); \
while (len--) { hash ^= *p++; hash *= 16777619U; } \
} while (0)
AFP_HASH_BYTES(&volume, sizeof(volume));
AFP_HASH_BYTES(&stb->st_dev, sizeof(stb->st_dev));
AFP_HASH_BYTES(&stb->st_ino, sizeof(stb->st_ino));
#undef AFP_HASH_BYTES
hash &= 0x7fffffffU;
if (!hash) hash = 1;
return(hash);
}
static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len,
uint8 *response)
{
uint8 dir_handle;
int path_len;
int volume;
char unixname[PATH_MAX];
struct stat stbuff;
uint32 entry_id = 0;
int result;
if (afp_len < 3) {
XDPRINTF((2,0, "AFP Get Entry ID From Path Name rejected: short request len=%d",
afp_len));
return(-0x7e); /* NCP Boundary Check Failed */
}
dir_handle = afp_req[1];
path_len = (int)afp_req[2];
if (path_len < 0 || afp_len < 3 + path_len) {
XDPRINTF((2,0, "AFP Get Entry ID From Path Name rejected: boundary check len=%d path_len=%d",
afp_len, path_len));
return(-0x7e);
}
if (!nwatalk_backend_available()) {
XDPRINTF((3,0, "AFP Get Entry ID From Path Name rejected: libatalk backend unavailable"));
return(-0xbf); /* invalid namespace */
}
volume = conn_get_kpl_unxname(unixname, sizeof(unixname),
(int)dir_handle, afp_req + 3, path_len);
if (volume < 0) {
XDPRINTF((2,0, "AFP Get Entry ID From Path Name path resolve failed: dh=%d path='%s' result=-0x%x",
(int)dir_handle, visable_data(afp_req + 3, path_len), -volume));
return(volume);
}
if (stat(unixname, &stbuff)) {
XDPRINTF((2,0, "AFP Get Entry ID From Path Name stat failed: dh=%d path='%s' unix='%s' errno=%d",
(int)dir_handle, visable_data(afp_req + 3, path_len),
unixname, errno));
return(-0x9c); /* Invalid Path */
}
result = nwatalk_get_entry_id(unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
U32_TO_BE32(entry_id, response);
XDPRINTF((3,0, "AFP Get Entry ID From Path Name: dh=%d path='%s' entry=0x%08x%s",
(int)dir_handle, visable_data(afp_req + 3, path_len), entry_id,
(result < 0) ? " fallback" : ""));
return(4);
}
static void afp_copy_fixed_name(uint8 *dst, int dst_len,
const uint8 *src, int src_len)
{
int i;
memset(dst, 0, dst_len);
if (!src || src_len < 1) return;
if (src_len > dst_len) src_len = dst_len;
for (i = 0; i < src_len; i++) {
uint8 c = src[i];
if (!c) break;
dst[i] = c;
}
}
static void afp_leaf_name_from_path(uint8 *dst, int dst_len,
const uint8 *path, int path_len)
{
int start = 0;
int end = path_len;
int i;
if (!path || path_len < 1) {
afp_copy_fixed_name(dst, dst_len, (const uint8 *)"", 0);
return;
}
while (end > 0 && (path[end - 1] == '/' || path[end - 1] == '\\'))
end--;
for (i = end - 1; i >= 0; i--) {
if (path[i] == ':' || path[i] == '/' || path[i] == '\\') {
start = i + 1;
break;
}
}
if (start >= end) {
int colon = -1;
for (i = 0; i < path_len; i++) {
if (path[i] == ':') {
colon = i;
break;
}
}
if (colon > 0) {
start = 0;
end = colon;
}
}
afp_copy_fixed_name(dst, dst_len, path + start, end - start);
}
static uint16 afp_basic_attributes(const struct stat *stb)
{
if (S_ISDIR(stb->st_mode))
return(0x0400); /* AFP_SA_SUBDIR */
return(0x0000); /* AFP_SA_NORMAL */
}
static uint16 afp_basic_access_privileges(const struct stat *stb)
{
if (S_ISDIR(stb->st_mode))
return(0x4000 | 0x2000 | 0x8000); /* search, parental, modify attrs */
return(0x0100 | 0x0200 | 0x0400 | 0x1000 | 0x8000); /* rw open delete modify */
}
static uint16 afp_count_offspring(const char *unixname, const struct stat *stb)
{
DIR *dir;
struct dirent *de;
uint32 count = 0;
if (!S_ISDIR(stb->st_mode)) return(0);
dir = opendir(unixname);
if (!dir) return(0);
while ((de = readdir(dir)) != NULL) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
if (count < 0xffff) count++;
}
closedir(dir);
return((uint16)count);
}
static int afp_get_file_information(uint8 *afp_req, int afp_len,
uint8 *response)
{
uint8 volume_number;
uint32 request_entry_id;
uint16 request_mask;
int path_len;
int volume;
char unixname[PATH_MAX];
struct stat stbuff;
uint32 entry_id = 0;
uint32 parent_id = 0;
uint32 resource_size = 0;
uint8 finder_info[NWATALK_FINDER_INFO_LEN];
uint8 empty_path = 0;
int result;
if (afp_len < 9) {
XDPRINTF((2,0, "AFP Get File Information rejected: short request len=%d",
afp_len));
return(-0x7e); /* NCP Boundary Check Failed */
}
volume_number = afp_req[1];
request_entry_id = GET_BE32(afp_req + 2);
request_mask = GET_BE16(afp_req + 6);
path_len = (int)afp_req[8];
if (path_len < 0 || afp_len < 9 + path_len) {
XDPRINTF((2,0, "AFP Get File Information rejected: boundary check len=%d path_len=%d",
afp_len, path_len));
return(-0x7e);
}
if (!nwatalk_backend_available()) {
XDPRINTF((3,0, "AFP Get File Information rejected: libatalk backend unavailable"));
return(-0xbf); /* invalid namespace */
}
if (!path_len) {
XDPRINTF((2,0, "AFP Get File Information rejected: entry-id-only lookup unsupported vol=%d entry=0x%08x mask=0x%04x",
(int)volume_number, request_entry_id, request_mask));
return(-0x9c); /* Invalid Path until persistent entry-id lookup exists */
}
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
path_len ? afp_req + 9 : &empty_path,
path_len);
if (volume < 0) {
XDPRINTF((2,0, "AFP Get File Information path resolve failed: vol=%d entry=0x%08x path='%s' result=-0x%x",
(int)volume_number, request_entry_id,
visable_data(afp_req + 9, path_len), -volume));
return(volume);
}
if (stat(unixname, &stbuff)) {
XDPRINTF((2,0, "AFP Get File Information stat failed: vol=%d entry=0x%08x path='%s' unix='%s' errno=%d",
(int)volume_number, request_entry_id,
visable_data(afp_req + 9, path_len), unixname, errno));
return(-0x9c); /* Invalid Path */
}
result = nwatalk_get_entry_id(unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
memset(response, 0, 120);
U32_TO_BE32(entry_id, response + 0);
U32_TO_BE32(parent_id, response + 4);
U16_TO_BE16(afp_basic_attributes(&stbuff), response + 8);
U32_TO_BE32(S_ISDIR(stbuff.st_mode) ? 0 : (uint32)stbuff.st_size, response + 10);
if (!S_ISDIR(stbuff.st_mode))
(void)nwatalk_get_resource_fork_size(unixname, &resource_size);
U32_TO_BE32(resource_size, response + 14);
U16_TO_BE16(afp_count_offspring(unixname, &stbuff), response + 18);
un_date_2_nw(stbuff.st_ctime, response + 20, 1);
un_date_2_nw(stbuff.st_atime, response + 22, 1);
un_date_2_nw(stbuff.st_mtime, response + 24, 1);
un_time_2_nw(stbuff.st_mtime, response + 26, 1);
U16_TO_BE16(0, response + 28); /* Backup Date */
U16_TO_BE16(0, response + 30); /* Backup Time */
memset(finder_info, 0, sizeof(finder_info));
(void)nwatalk_get_finder_info(unixname, finder_info, sizeof(finder_info));
memcpy(response + 32, finder_info, NWATALK_FINDER_INFO_LEN);
afp_leaf_name_from_path(response + 64, 32, afp_req + 9, path_len);
U32_TO_BE32(get_file_owner(&stbuff), response + 96);
afp_leaf_name_from_path(response + 100, 12, afp_req + 9, path_len);
U16_TO_BE16(afp_basic_access_privileges(&stbuff), response + 112);
/* ProDOS info at offset 114 stays zero until a real Mac namespace maps it. */
XDPRINTF((3,0, "AFP Get File Information: vol=%d entry=0x%08x mask=0x%04x path='%s' reply_entry=0x%08x%s",
(int)volume_number, request_entry_id, request_mask,
visable_data(afp_req + 9, path_len), entry_id,
(result < 0) ? " fallback" : ""));
return(120);
}
static int handle_ncp_serv(void)
{
int function = (int)ncprequest->function;
int completition = 0; /* first set */
int org_nw_debug = nw_debug;
int do_druck = 0;
int data_len = 0;
if (last_sequence == (int)ncprequest->sequence
&& ncp_type != 0x1111){ /* send the same again */
if (t_sndudata(FD_NCP_OUT, &ud) < 0) {
if (nw_debug) t_error("t_sndudata !OK");
}
XDPRINTF((3,0, "Sequence %d is written twice", (int)ncprequest->sequence));
return(0);
}
req_printed=0;
if (nw_debug > 1){
if (nw_debug < 10
&& (function==0x48 || function == 0x49)) /* read or write */
nw_debug=1;
if (nw_debug < 15
&& (function==0x48)) /* read */
nw_debug=1;
}
if (nw_debug > 5) pr_debug_request();
if (ncp_type == 0x2222) {
switch (function) {
/* 0.99.pl18, 25-Sep-99
* these log/lock functions are not well tested and perhaps not
* ok .
*/
case 0x3 : { /* Log File */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dir_handle;
uint8 lock_flag; /* 0 = log, 1 = lock excl */
/* 3 = lock shared */
uint8 timeout[2]; /* HI LO */
uint8 len;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
int result = nw_log_file( input->lock_flag,
GET_BE16(input->timeout),
input->dir_handle,
input->len,
input->path);
if (result) completition = (uint8) -result;
}
break;
case 0x4 : /* Lock File Set (old) */
case 0x6a : { /* Lock File Set */
/*
* NCP 0x2222/04 Lock File Set (old) and NCP 0x2222/106 Lock
* File Set lock all files logged by the calling client's current
* task.
*
* SDK request: 2-byte Lock Timeout, in units of 1/18 second;
* 0 means No Wait.
* SDK reply: no reply data.
* SDK completion: 0x00 success, 0xfe timeout, 0xff lock error.
*
* The newer 0x6a call replaces the old 0x04 call but has the same
* request layout for the file set lock itself. MARS-NWE records
* file-set members through Log File (0x03), then uses the shared
* set handler for Lock/Release/Clear File Set operations.
*/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 timeout[2]; /* HI LO */
} *input = (struct INPUT *) (ncprequest);
int result = share_handle_lock_sets(
1, /* File Set */
0, /* Lock logged entries */
GET_BE16(input->timeout));
if (result) completition = (uint8) -result;
}
break;
case 0x5 : /* Release File */
case 0x7 : { /* Clear File, removes file from logset */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dir_handle;
uint8 len;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
int result = nw_log_file( (function==0x7)
? -2 /* unlock and unlog */
: -1, /* unlock */
0,
input->dir_handle,
input->len,
input->path);
if (result) completition = (uint8) -result;
}
break;
case 0x6 : /* Release File Set */
case 0x8 : { /* Clear File Set */
int result = share_handle_lock_sets(
1, /* File Set */
(function==0x8)
? -2 /* Clear */
: -1, /* Release */
0);
if (result) completition = (uint8) -result;
}
break;
case 0x9 : { /* Log Logical Record */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 lock_flag; /* 0 = log */
/* 1 = exclusive lock */
/* 3 = shared ro lock */
uint8 timeout[2]; /* HI LO */
uint8 len; /* synch name len */
uint8 name[2]; /* synch name */
} *input = (struct INPUT *) (ncprequest);
int result = nw_log_logical_record(
(int) input->lock_flag,
(int) GET_BE16(input->timeout),
(int) input->len,
input->name);
if (result) completition = (uint8) -result;
}
break;
case 0xa : { /* Log Logical Record Set */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 lock_flag; /* 0 = log */
/* 1 = exclusive lock */
/* 3 = shared ro lock */
uint8 timeout[2]; /* HI LO */
} *input = (struct INPUT *) (ncprequest);
int result = share_handle_lock_sets(
2, /* Logical Record Set */
(int) input->lock_flag,
(int) GET_BE16(input->timeout) );
if (result) completition = (uint8) -result;
}
break;
case 0xb : /* Clear Logical Record */
case 0xc : { /* Release Logical Record */
/*
* SDK: NCP 0x2222/11 Clear Logical Record removes one logical
* record from the calling task's synchronization table.
*
* SDK: NCP 0x2222/12 Release Logical Record releases/unlocks one
* synchronization string held by the calling client. The request
* is a one-byte Synch Name Length followed by the Synch Name
* (up to 128 bytes), returns no reply data, and reports success or
* Unlock Error through the completion code.
*
* Important semantic difference: Release Logical Record leaves the
* string in the caller's synchronization string table so a later
* Lock Logical Record Set can relock it. Clear Logical Record
* unlocks and removes it from the table.
*/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 len; /* synch name len */
uint8 name[2]; /* synch name */
} *input = (struct INPUT *) (ncprequest);
int result = nw_log_logical_record(
(function == 0xb)
? -2 /* unlock + unlog */
: -1, /* unlock */
0,
(int) input->len,
input->name);
if (result) completition = (uint8) -result;
}
break;
case 0xe : /* Clear Logical Record Set */
case 0xd : { /* Release Logical Record Set */
int result = share_handle_lock_sets(
2, /* Logical Record Set */
(function==0xe)
? -2 /* Clear */
: -1, /* Release */
0);
if (result) completition = (uint8) -result;
}
break;
case 0x12 : { /* Get Volume Info with Number */
int volume = (int)*requestdata;
struct XDATA {
uint8 sec_per_block[2];
uint8 total_blocks[2];
uint8 avail_blocks[2];
uint8 total_dirs[2];
uint8 avail_dirs[2];
uint8 name[16];
uint8 removable[2];
} *xdata = (struct XDATA*) responsedata;
int result;
memset(xdata, 0, sizeof(struct XDATA));
if ((result = nw_get_volume_name(volume, xdata->name, sizeof(xdata->name)))>-1){
struct fs_usage fsp;
if (!nw_get_fs_usage(xdata->name, &fsp,
entry8_flags&0x40 )) {
int sector_scale=1;
while (fsp.fsu_blocks/sector_scale > 0xffff)
sector_scale+=2;
U16_TO_BE16(sector_scale, xdata->sec_per_block);
U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks);
U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks);
U16_TO_BE16(fsp.fsu_files, xdata->total_dirs);
U16_TO_BE16(fsp.fsu_ffree, xdata->avail_dirs);
if ( get_volume_options(volume) & VOL_OPTION_REMOUNT) {
U16_TO_BE16(1, xdata->removable);
} else {
U16_TO_BE16(0, xdata->removable);
}
}
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
}
break;
case 0x13 : { /* Get Station Number */
/*
* This is the old one-byte connection number call used by
* DOS requesters before the richer NCP 23 connection services.
* The SDK describes the client-visible API as
* NWGetConnectionNumber(); older low-level documentation names the
* underlying simple NCP 0x13 "Get Station Number".
*/
if (act_connection > 0 && act_connection < 256) {
*responsedata=(uint8) act_connection;
data_len = 1;
} else {
XDPRINTF((1, 0, "Get Station Number failed, conn=%d",
act_connection));
completition = 0xff;
}
}
break;
case 0x14 : { /* GET DATE und TIME */
struct SERVER_DATE {
uint8 year;
uint8 mon;
uint8 day;
uint8 std;
uint8 min;
uint8 sec;
uint8 day_of_week;
} *mydate = (struct SERVER_DATE*) responsedata;
struct tm *s_tm;
time_t timer;
time(&timer);
s_tm = localtime(&timer);
mydate->year = (uint8) s_tm->tm_year;
mydate->mon = (uint8) s_tm->tm_mon+1;
mydate->day = (uint8) s_tm->tm_mday;
mydate->std = (uint8) s_tm->tm_hour;
mydate->min = (uint8) s_tm->tm_min;
mydate->sec = (uint8) s_tm->tm_sec;
mydate->day_of_week = (uint8) s_tm->tm_wday; /* Wochentag */
data_len = sizeof(struct SERVER_DATE);
}
break;
case 0x15 :
return(-1); /* nwbind must do this call */
case 0x16 : {
/* uint8 len = *(requestdata+1); */
uint8 *p = requestdata +2;
switch (*p) {
case 0 : {
/******** SetDirektoryHandle *************/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 target_dir_handle; /* to change */
uint8 source_dir_handle;
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
completition =
(uint8)-nw_set_dir_handle((int)input->target_dir_handle,
(int)input->source_dir_handle,
input->path,
(int)input->pathlen,
(int)(ncprequest->task));
}
break;
case 0x1 : {
/******** GetDirektoryPATH ***************/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle;
} *input = (struct INPUT *) (ncprequest);
struct XDATA {
uint8 len;
uint8 name[256];
} *xdata = (struct XDATA*) responsedata;
int result = nw_get_directory_path((int)input->dir_handle,
xdata->name, sizeof(xdata->name));
if (result > -1){
xdata->len = (uint8) result;
data_len = result + 1;
xdata->name[result] = '\0';
XDPRINTF((5,0, "GetDirektoryPATH=%s", xdata->name));
} else completition = (uint8)-result;
}
break;
case 0x2 : { /* Scan Direktory Information */
/******** Scan Dir Info ****************/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Verzeichnis Handle */
uint8 sub_dir_nmbr[2]; /* HI LOW */
/* firsttime 1 */
uint8 len; /* kann auch 0 sein */
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
struct XDATA {
uint8 sub_dir_name[16];
uint8 create_date_time[4];
uint8 owner_id[4]; /* HI LOW */
uint8 max_right_mask; /* inherited right mask */
uint8 reserved; /* Reserved by Novell */
uint8 sub_dir_nmbr[2]; /* HI LOW */
} *xdata = (struct XDATA*) responsedata;
int result;
memcpy(xdata->sub_dir_nmbr, input->sub_dir_nmbr, 2);
result = nw_scan_dir_info((int)input->dir_handle,
input->path, (int)input->len,
xdata->sub_dir_nmbr, xdata->sub_dir_name,
xdata->create_date_time, xdata->owner_id);
if (result > -1){
xdata->max_right_mask = (uint8)result;
data_len = sizeof(struct XDATA);
XDPRINTF((5,0,"Scan Dir Info max_right_mask=%d", (int)result));
} else completition = (uint8)-result;
}
break;
case 0x3 : { /* Get Direktory Rights */
/******** Get Eff Dir Rights ****************/
struct XDATA {
uint8 eff_right_mask; /* Effektive Right to Dir, old! */
} *xdata = (struct XDATA*) responsedata;
int result = nw_get_eff_dir_rights(
(int)*(p+1),
p+3,
(int)*(p+2), 0);
if (result > -1) {
int ncp22_rights = trustee_v3_to_ncp22_rights(result);
xdata->eff_right_mask = (uint8)ncp22_rights;
data_len = 1;
XDPRINTF((5,0,"NCP22/3 Get Dir Rights=%d ncp22=%d",
(int)result, (int)ncp22_rights));
} else completition = (uint8) -result;
}
break;
case 0x4 : { /* Modify Max Right Mask */
/******** MODIFY MAX RIGHT MASK ****************/
/*
* Request (NetWare 2.x compatible NCP 22/4):
* byte directory handle
* byte grant rights mask
* byte revoke rights mask
* byte path length
* path bytes
*
* New mask is: (old_mask | grant_mask) & ~revoke_mask.
*
* No reply data.
*/
int result = nw_modify_max_right_mask(
(int)*(p+1),
p+5,
(int)*(p+4),
(int)*(p+2),
(int)*(p+3));
if (result) completition = (uint8)-result;
}
break;
case 0x5 : { /* Get Volume Number 0 .. 31 */
/******** GetVolume Number ***************/
/* p+1 = namelen */
/* p+2 = data z.b 'SYS' */
struct XDATA {
uint8 volume; /* Nummer */
} *xdata = (struct XDATA*) responsedata;
int result = nw_get_volume_number(p+2, (int)*(p+1));
if (result > -1) {
xdata->volume = (uint8) result;
data_len = 1;
} else completition = (uint8) -result;
}
break;
case 0x6 : { /* Get Volume Name from 0 .. 31 */
/******** Get Volume Name ***************/
struct XDATA {
uint8 namelen;
uint8 name[16];
} *xdata = (struct XDATA*) responsedata;
int result = nw_get_volume_name((int)*(p+1),
xdata->name, sizeof(xdata->name));
if (result > -1) {
xdata->namelen = (uint8) result;
data_len = result+1;
} else completition = (uint8) -result;
}
break;
case 0xa : { /* create directory */
/******** Create Dir *********************/
int dir_handle = (int) *(p+1);
#if 0
int rightmask = (int) *(p+2);
#endif
int pathlen = (int) *(p+3);
uint8 *path = p+4;
int code = nw_mk_rd_dir(dir_handle, path, pathlen, 1);
if (code) completition = (uint8) -code;
}
break;
case 0xb : { /* delete dirrctory */
/******** Delete DIR *********************/
int dir_handle = (int) *(p+1);
#if 0
int reserved = (int) *(p+2); /* Res. by NOVELL */
#endif
int pathlen = (int) *(p+3);
uint8 *path = p+4;
int code = nw_mk_rd_dir(dir_handle, path, pathlen, 0);
if (code) completition = (uint8) -code;
}
break;
case 0xd : { /* Add Trustees to DIR */
/******** AddTrustesstoDir ***************/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Verzeichnis Handle */
uint8 trustee_id[4]; /* Trustee Object ID */
uint8 trustee_right_mask;
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
uint32 trustee_id = GET_BE32(input->trustee_id);
int ncp22_rights = (int)input->trustee_right_mask;
int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights);
int copylen = input->pathlen;
uint8 pathbuf[256];
int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
int result;
ncp23_debug_dump("NCP22/0D AddTrustee raw",
(uint8 *)&input->dir_handle, raw_len);
if (copylen > 255) copylen = 255;
memcpy(pathbuf, input->path, copylen);
pathbuf[copylen] = '\0';
result = nw_add_trustee(
input->dir_handle,
input->path, input->pathlen,
trustee_id,
trustee_rights,
0);
XDPRINTF((5,0,
"NCP22/0D AddTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%02x rights=0x%03x rc=%d",
input->dir_handle, input->pathlen, pathbuf,
(unsigned long)trustee_id, ncp22_rights, trustee_rights, result));
if (result) completition = (uint8) -result;
}
break;
case 0xe : { /* remove trustees */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Handle */
uint8 trustee_id[4]; /* Trustee Object ID */
uint8 reserved;
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
uint32 trustee_id = GET_BE32(input->trustee_id);
int copylen = input->pathlen;
uint8 pathbuf[256];
int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
int result;
ncp23_debug_dump("NCP22/0E DelTrustee raw",
(uint8 *)&input->dir_handle, raw_len);
if (copylen > 255) copylen = 255;
memcpy(pathbuf, input->path, copylen);
pathbuf[copylen] = '\0';
result = nw_del_trustee(
input->dir_handle,
input->path, input->pathlen,
trustee_id,
0); /* normal */
XDPRINTF((5,0,
"NCP22/0E DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d",
input->dir_handle, input->pathlen, pathbuf,
(unsigned long)trustee_id, result));
if (result) completition = (uint8) -result;
}
break;
case 0xf : { /* rename dir */
/******** Rename DIR *********************/
int dir_handle = (int) *(p+1);
int oldpathlen = (int) *(p+2);
uint8 *oldpath = p+3;
int newpathlen = (int) *(oldpath + oldpathlen);
uint8 *newpath = oldpath + oldpathlen + 1;
int code = mv_dir(dir_handle,
oldpath, oldpathlen,
newpath, newpathlen);
if (code) completition = (uint8) -code;
}
break;
case 0x12 : /* Allocate Permanent Dir Handle */
/******** Allocate Permanent DIR Handle **/
case 0x13 : /* Allocate Temp Dir Handle */
/******** Allocate Temp DIR Handle **/
case 0x16 : { /* Allocate Special Temp Dir Handle */
/******** Allocate spez temp DIR Handle **/
struct XDATA {
uint8 dirhandle; /* new Dir Handle */
uint8 right_mask; /* 0xff effektive Right MAsk ? */
} *xdata = (struct XDATA*) responsedata;
int eff_rights;
int dirhandle = nw_alloc_dir_handle(
(int) *(p+1),
p+4,
(int)*(p+3),
(int)*(p+2),
(*p==0x12) ? 0
: ((*p==0x13) ? 1 : 2),
(int)(ncprequest->task),
&eff_rights);
if (dirhandle > -1){
xdata->dirhandle = (uint8) dirhandle;
xdata->right_mask = eff_rights;
data_len = sizeof(struct XDATA);
} else completition = (uint8) -dirhandle;
}
break;
case 0x14 : { /* deallocate Dir Handle */
/******** Free DIR Handle ****************/
int err_code = nw_free_dir_handle((int)*(p+1),
(int)(ncprequest->task));
if (err_code) completition = (uint8) -err_code;
}
break;
case 0x15 : { /* liefert Volume Information */
/******** Get Volume Info with Handle ****/
struct XDATA {
uint8 sectors[2];
uint8 total_blocks[2];
uint8 avail_blocks[2];
uint8 total_dirs[2]; /* anz dirs */
uint8 avail_dirs[2]; /* free dirs */
uint8 name[16]; /* SYS Name */
uint8 removable[2];
} *xdata = (struct XDATA*)responsedata;
int result = nw_get_vol_number((int)*(p+1));
memset(xdata, 0, sizeof(struct XDATA));
if (result > -1) {
int volume = result;
result = nw_get_volume_name(volume,
xdata->name, sizeof(xdata->name) );
if (result > -1) {
struct fs_usage fsp;
if (!nw_get_fs_usage(xdata->name, &fsp,
entry8_flags&0x40 )) {
int sector_scale=1;
while (fsp.fsu_blocks/sector_scale > 0xffff)
sector_scale+=2;
U16_TO_BE16(sector_scale, xdata->sectors);
U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks);
U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks);
U16_TO_BE16(fsp.fsu_files, xdata->total_dirs);
U16_TO_BE16(fsp.fsu_ffree, xdata->avail_dirs);
if (get_volume_options(volume) & VOL_OPTION_REMOUNT) {
U16_TO_BE16(1, xdata->removable);
} else {
U16_TO_BE16(0, xdata->removable);
}
}
data_len = sizeof(struct XDATA);
XDPRINTF((5,0,"GIVE VOLUME INFO from :%s:", xdata->name));
result = 0;
}
}
completition = (uint8)-result;
}
break;
case 0x17 : { /* Extract a Base Handle */
/*
* NCP 0x2222/22/23 Extract a Base Handle.
*
* WebSDK: request carries DirectoryHandle. Reply returns a
* 10-byte ServerNetworkAddress plus a 4-byte opaque
* HandleID. The handle must have been allocated with Alloc
* Permanent Directory Handle. The returned HandleID is used
* by NCP 0x16/0x18 Restore an Extracted Base Handle.
*
* MARS-NWE keeps the HandleID opaque to the client and maps
* it to the current connection's saved volume/path tuple.
*/
struct XDATA {
uint8 server_network_address[10];
uint8 handle_id[4];
} *xdata = (struct XDATA*)responsedata;
int result;
memcpy(xdata->server_network_address, my_addr.net, 4);
memcpy(xdata->server_network_address+4, my_addr.node, 6);
result = nw_extract_base_handle((int)*(p+1), xdata->handle_id);
if (result) completition = (uint8)-result;
else data_len = sizeof(struct XDATA);
}
break;
case 0x18 : { /* Restore Directory Handle */
/*
* NCP 0x2222/22/24 Restore an Extracted Base Handle.
*
* WebSDK: request carries the 10-byte ServerNetworkAddress
* and 4-byte HandleID returned by Extract a Base Handle.
* Reply returns NewDirectoryHandle and AccessRightsMask.
*
* The old SDK API exposes this as NWRestoreDirectoryHandle()
* and uses a 14-byte save buffer produced by
* NWSaveDirectoryHandle(). Rust nwserver and lwared do not
* implement this older save/restore pair; newer clients
* typically use the ordinary allocate/set directory-handle
* calls instead. Keep this implementation conservative and
* connection-local rather than guessing a global NetWare
* directory-base number.
*/
struct INPUT {
uint8 server_network_address[10];
uint8 handle_id[4];
} *input = (struct INPUT*)(p+1);
struct XDATA {
uint8 dirhandle;
uint8 right_mask;
} *xdata = (struct XDATA*)responsedata;
int eff_rights = 0;
int result;
if (memcmp(input->server_network_address, my_addr.net, 4)
|| memcmp(input->server_network_address+4, my_addr.node, 6)) {
completition = 0xfd; /* Bad Station Number */
break;
}
result = nw_restore_base_handle(input->handle_id,
(int)(ncprequest->task), &eff_rights);
if (result > -1) {
xdata->dirhandle = (uint8)result;
xdata->right_mask = (uint8)eff_rights;
data_len = sizeof(struct XDATA);
} else completition = (uint8)-result;
}
break;
case 0x19 : {
/* Set Directory Information
* Modifies basic directory information as creation date and
* directory rights mask. DOS namespace.
*/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle;
uint8 creation_date[2];
uint8 creation_time[2];
uint8 owner_id[4];
uint8 new_max_rights;
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
int result = nw_set_dir_info(
input->dir_handle,
input->path, input->pathlen,
GET_BE32(input->owner_id),
(int)input->new_max_rights,
input->creation_date,
input->creation_time);
if (result<0) completition = (uint8) -result;
/* No REPLY */
}
break;
case 0x1a : { /* Get Pathname of A Volume Dir Pair */
/*
* Old NetWare 2.x compatibility call:
* byte volume
* word directory entry number (Hi-Lo)
*
* Reply:
* byte path length
* byte path[path length]
*
* This is the older string form of the NCP23/F3 mapping.
* NCP23/F3 is preferred for 32-bit directory numbers and
* namespace-aware callers.
*/
struct XDATA {
uint8 pathlen;
uint8 pathname[255];
} *xdata = (struct XDATA*)responsedata;
int volume = (int)*(p+1);
uint32 dirnum = (uint32)GET_BE16(p+2);
uint8 comps[256];
uint8 pathbuf[255];
int result;
int pos = 0;
int out = 0;
int first = 1;
result = map_directory_number_to_path(volume, dirnum, 0,
comps, sizeof(comps));
if (result > -1) {
while (pos < result) {
int l = (int)comps[pos++];
if (l <= 0 || pos + l > result) {
result = -0x9c;
break;
}
if (!first) {
if (out >= (int)sizeof(pathbuf)) {
result = -0x9c;
break;
}
pathbuf[out++] = '\\';
}
if (out + l > (int)sizeof(pathbuf)) {
result = -0x9c;
break;
}
memcpy(pathbuf + out, comps + pos, l);
out += l;
pos += l;
first = 0;
}
}
if (result > -1) {
xdata->pathlen = (uint8)out;
memcpy(xdata->pathname, pathbuf, out);
data_len = out + 1;
} else completition = (uint8)(-result);
}
break;
case 0x1e : { /* SCAN a Directory, e.g. used by ndir.exe */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Verzeichnis Handle */
uint8 attrib; /* Search Attrib z.B. 0x6 */
uint8 searchsequence[4]; /* 32 bit */
uint8 len;
uint8 data[2];
} *input = (struct INPUT *) (ncprequest);
int result = nw_scan_a_directory(
responsedata,
input->dir_handle,
input->data,
input->len,
input->attrib,
GET_BE32(input->searchsequence));
if (result > -1) data_len = result;
else completition = (uint8) (-result);
}
break;
case 0x1f : { /* SCAN a root dir ???? */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Verzeichnis Handle */
uint8 dont_know1; /* ???? 0xc0 */
uint8 dont_know2; /* ???? 0xfa */
} *input = (struct INPUT *) (ncprequest);
int result = nw_scan_a_root_dir(
responsedata,
input->dir_handle);
if (result > -1) data_len = result;
else completition = (uint8) (-result);
}
break;
case 0x20 : { /* Scan Volume's User Disk Restrictions */
/*
* NCP 0x2222/22/32 Scan Volume's User Disk Restrictions.
*
* WebSDK / headers:
* Request: Volume Number, Sequence. Sequence starts at
* zero and is incremented by the number of entries returned
* by previous calls.
* Reply: Number Of Entries followed by up to twelve
* Object ID / Restriction pairs. Restrictions are reported
* in 4K blocks.
*
* The Rust nwserver and lwared implementations both return
* an empty list after validating the volume. MARS-NWE keeps
* that SDK-compatible unrestricted result for non-quota
* builds, and fills entries from the existing Linux quota
* backend when QUOTA_SUPPORT is enabled and a restricted
* bindery user maps to a Unix uid.
*/
uint8 volnr = *(p+1);
uint32 sequence = GET_BE32(p+2);
int result = scan_volume_user_disk_restrictions(
volnr, sequence, responsedata);
if (result > -1) data_len = result;
else completition = (uint8) (-result);
}
break;
case 0x21 : { /* change Vol restrictions for Obj */
XDPRINTF((5, 0, "Change vol restrictions"));
}
return(-2); /* nwbind must do prehandling */
case 0x22 : { /* remove Vol restrictions for Obj */
XDPRINTF((5, 0, "Remove vol restrictions"));
}
return(-2); /* nwbind must do prehandling */
case 0x25 : { /* Set Entry, Set Directory File Information
* sets or changes the file or directory information to the
* values entered in 'Change Bits'.
* NO REPLY
* used by ncopy.exe, flag.exe
*/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle;
uint8 attrib;
NW_SET_DIR_INFO f;
} *input = (struct INPUT *) (ncprequest);
int result = nw_set_a_directory_entry(
input->dir_handle,
input->f.u.f.name,
input->f.u.f.namlen,
input->attrib,
GET_BE32(input->f.searchsequence),
&(input->f));
if (result < 0) {
completition = (uint8)(-result);
XDPRINTF((5,0,
"NCP22/25 SetDirFileInfo: dh=%d attr=0x%02x seq=0x%08lx result=%d cc=0x%02x",
(int)input->dir_handle,
(int)input->attrib,
(unsigned long)GET_BE32(input->f.searchsequence),
result,
(unsigned int)completition));
}
}
break;
case 0x26 : { /* Scan file or Dir for ext trustees */
int sequence = (int)*(p+2); /* trustee sequence */
struct XDATA {
uint8 entries;
uint8 ids[80]; /* 20 id's */
uint8 trustees[40]; /* 20 trustees's */
} *xdata = (struct XDATA*) responsedata;
uint32 ids[20];
int trustees[20];
int result = nw_scan_for_trustee(
(int)*(p+1), /* dir handle */
sequence,
p+4, /* path */
(int)*(p+3), /* pathlen */
20, /* max entries */
ids,
trustees,
1); /* extended */
if (result > -1) {
int i = -1;
uint8 *idsp = xdata->ids;
uint8 *trp = xdata->trustees;
memset(xdata, 0, sizeof(*xdata));
xdata->entries = result;
while(++i < result) {
int ncp22_rights = trustee_v3_to_ncp22_rights(trustees[i]);
U32_TO_BE32(ids[i], idsp);
idsp+=4;
U16_TO_16(ncp22_rights, trp); /* LO - HI */
trp+=2;
}
data_len = sizeof(struct XDATA);
} else completition = (uint8) (-result);
}
break;
case 0x27 : { /* Add Ext Trustees to DIR or File */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Handle */
uint8 trustee_id[4]; /* Trustee Object ID */
uint8 trustee_rights[2]; /* lo - hi */
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
uint32 trustee_id = GET_BE32(input->trustee_id);
int ncp22_rights = GET_16(input->trustee_rights);
int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights);
int copylen = input->pathlen;
uint8 pathbuf[256];
int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
int result;
ncp23_debug_dump("NCP22/27 SetTrustee raw",
(uint8 *)&input->dir_handle, raw_len);
if (copylen > 255) copylen = 255;
memcpy(pathbuf, input->path, copylen);
pathbuf[copylen] = '\0';
result = nw_add_trustee(
input->dir_handle,
input->path, input->pathlen,
trustee_id,
trustee_rights,
1); /* extended */
XDPRINTF((5,0,
"NCP22/27 SetTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%04x rights=0x%03x rc=%d",
input->dir_handle, input->pathlen, pathbuf,
(unsigned long)trustee_id, ncp22_rights, trustee_rights, result));
if (result) completition = (uint8) -result;
}
break;
case 0x28 : { /* Scan File Physical ??? */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* directory handle */
uint8 attrib; /* Search Attrib ?? 0x2f */
uint8 searchsequence[4]; /* 32 bit */
uint8 len;
uint8 data[2];
} *input = (struct INPUT *) (ncprequest);
/* we try, whether this is ok ????? */
int result = nw_scan_a_directory(
responsedata,
input->dir_handle,
input->data,
input->len,
input->attrib,
GET_BE32(input->searchsequence));
if (result > -1) data_len = result;
else completition = (uint8) (-result);
}
break;
/*
* NCP 0x2222/22/41 Get Object Disk Usage And Restrictions.
*
* WebSDK / headers:
* Request: Volume Number, Object ID (high-low)
* Reply: Restriction and In Use, both 32-bit values in 4K
* blocks.
*
* A restriction value of 0x40000000 means that the object has no
* disk restriction. NetWare also treats unknown/invalid object
* IDs as a successful unrestricted/no-use result.
*
* With QUOTA_SUPPORT enabled this call needs bindery prehandling:
* nwbind maps the Object ID to a Unix uid before
* handle_after_bind() calls nw_get_vol_restrictions(). Without
* quota support, keep the SDK-compatible fallback local so builds
* on hosts without quota support do not need the quota backend.
*/
case 0x29 : { /* Get Object Disk Usage And Restrictions */
#if QUOTA_SUPPORT
XDPRINTF((5, 0, "Read vol restrictions"));
return(-2); /* nwbind must do prehandling */
#else
#if DO_DEBUG
uint8 volnr = *(p+1);
uint32 id = GET_BE32(p+2);
#endif
struct XDATA {
uint8 restriction[4];
uint8 inuse[4];
} *xdata = (struct XDATA*) responsedata;
XDPRINTF((5,0, "Get vol restriction fallback vol=%d, id=0x%lx",
(int)volnr, id));
U32_TO_32(0x40000000, xdata->restriction);
U32_TO_32(0x0, xdata->inuse);
data_len=sizeof(struct XDATA);
#endif
}
break;
case 0x2a : { /* Get Eff. Rights of DIR's and Files */
struct XDATA {
uint8 eff_rights[2]; /* LO-HI */
} *xdata = (struct XDATA*) responsedata;
int dir_handle = (int)*(p+1);
int pathlen = (int)*(p+2);
uint8 *path = p+3;
int copylen = pathlen;
uint8 pathbuf[256];
int result;
if (copylen > 255) copylen = 255;
memcpy(pathbuf, path, copylen);
pathbuf[copylen] = '\0';
result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
if (result > -1){
int ncp22_rights = trustee_v3_to_ncp22_rights(result);
U16_TO_16(ncp22_rights, xdata->eff_rights);
data_len = sizeof(struct XDATA);
XDPRINTF((5,0,
"NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rights=0x%04x ncp22=0x%04x",
dir_handle, pathlen, pathbuf, result, ncp22_rights));
} else {
completition = (uint8) (-result);
XDPRINTF((5,0,
"NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rc=%d",
dir_handle, pathlen, pathbuf, result));
}
}
break;
case 0x2b : { /* remove ext trustees */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 dir_handle; /* Handle */
uint8 trustee_id[4]; /* Trustee Object ID */
uint8 reserved;
uint8 pathlen;
uint8 path[2];
} *input = (struct INPUT *) (ncprequest);
uint32 trustee_id = GET_BE32(input->trustee_id);
int copylen = input->pathlen;
uint8 pathbuf[256];
int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff);
int result;
ncp23_debug_dump("NCP22/2B DelTrustee raw",
(uint8 *)&input->dir_handle, raw_len);
if (copylen > 255) copylen = 255;
memcpy(pathbuf, input->path, copylen);
pathbuf[copylen] = '\0';
result = nw_del_trustee(
input->dir_handle,
input->path, input->pathlen,
trustee_id,
1); /* extended */
XDPRINTF((5,0,
"NCP22/2B DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d",
input->dir_handle, input->pathlen, pathbuf,
(unsigned long)trustee_id, result));
if (result) completition = (uint8) -result;
}
break;
case 0x2c : { /* Get Volume and Purge Information */
/* new Call since V3.11 */
/* ncpfs need this call */
int volume = (int) *(p+1);
struct XDATA {
uint8 total_blocks[4]; /* LOW-HI !! */
uint8 avail_blocks[4];
uint8 purgeable_blocks[4];
uint8 not_purgeable_blocks[4];
uint8 total_dirs[4];
uint8 avail_dirs[4];
uint8 reserved_by_novell[4];
uint8 sec_per_block;
uint8 namlen;
uint8 name[1];
} *xdata = (struct XDATA*) responsedata;
uint8 name[100];
int result = nw_get_volume_name(volume, name, sizeof(name));
if (result > -1){
struct fs_usage fsp;
memset(xdata, 0, sizeof(struct XDATA));
if (!nw_get_fs_usage(name, &fsp, 0)) {
xdata->sec_per_block = 8; /* hard coded */
U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks);
U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks);
U32_TO_32(fsp.fsu_files, xdata->total_dirs);
U32_TO_32(fsp.fsu_ffree, xdata->avail_dirs);
}
xdata->namlen = strlen((char*)name);
strmaxcpy(xdata->name, name, xdata->namlen);
data_len = xdata->namlen + 30;
} else completition = (uint8) -result;
}
break;
case 0x2d : { /* Get Direktory Information */
int dir_handle = (int) *(p+1);
struct XDATA {
uint8 total_blocks[4];
uint8 avail_blocks[4];
uint8 total_dirs[4];
uint8 avail_dirs[4];
uint8 reserved_by_novell[4];
uint8 sec_per_block;
uint8 namlen;
uint8 name[1]; /* Volume Name */
} *xdata = (struct XDATA*) responsedata;
int result = nw_get_vol_number(dir_handle);
uint8 name[100];
if (result > -1)
result = nw_get_volume_name(result, name, sizeof(name));
if (result > -1) {
struct fs_usage fsp;
memset(xdata, 0, sizeof(struct XDATA));
if (!nw_get_fs_usage(name, &fsp, 0)) {
xdata->sec_per_block = 8; /* hard coded */
U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks);
U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks);
U32_TO_32(fsp.fsu_files, xdata->total_dirs);
U32_TO_32(fsp.fsu_ffree, xdata->avail_dirs);
}
xdata->namlen = strlen((char*)name);
strmaxcpy(xdata->name, name, xdata->namlen);
data_len = xdata->namlen + 22;
} else completition = (uint8) -result;
}
break;
case 0x2e : { /* Rename or Move (old) */
/*
* NCP 22 / subfunction 46 (0x2e): Rename Or Move (old)
*
* Request:
* byte source directory handle
* byte search attributes
* byte source path component count
* source path components: byte len + bytes, repeated
* byte destination directory handle
* byte destination path component count
* destination path components: byte len + bytes, repeated
*
* No reply data. This old call uses component-counted
* paths, unlike the plain length-prefixed NCP 69 rename.
*/
uint8 srcpath[256];
uint8 dstpath[256];
int src_handle = (int)*(p+1);
int searchattr = (int)*(p+2);
int src_count = (int)*(p+3);
int used = 0;
int srclen;
int dstlen;
int result;
uint8 *q;
srclen = ncp22_component_path_to_dos(p+4, src_count,
srcpath, sizeof(srcpath),
&used);
if (srclen < 0) {
completition = (uint8)(-srclen);
break;
}
q = p + 4 + used;
dstlen = ncp22_component_path_to_dos(q+2, (int)*(q+1),
dstpath, sizeof(dstpath),
NULL);
if (dstlen < 0) {
completition = (uint8)(-dstlen);
break;
}
XDPRINTF((5,0,
"NCP22/2E RenameOrMove: src_h=%d dst_h=%d attr=0x%02x src=`%s` dst=`%s`",
src_handle, (int)*q, searchattr, srcpath, dstpath));
result = nw_mv_files(searchattr,
src_handle, srcpath, srclen,
(int)*q, dstpath, dstlen);
XDPRINTF((5,0, "NCP22/2E RenameOrMove: file_result=%d", result));
/*
* nw_mv_files() is correct for file rename/move. For a
* directory source it may return either Invalid Path (0x9c)
* or "not found" (0xff), depending on the search attribute
* shape. Directories need the directory mover, and
* NCP22/2E can provide different source/destination handles.
*/
if (result == -0x9c || result == -0xff) {
int dir_result = nw_mv_dir_between_handles(src_handle,
srcpath, srclen,
(int)*q, dstpath, dstlen);
XDPRINTF((5,0,
"NCP22/2E RenameOrMove: directory fallback result=%d",
dir_result));
result = dir_result;
}
if (result < 0) completition = (uint8)(-result);
}
break;
#if WITH_NAME_SPACE_CALLS
case 0x2f : { /* Fill namespace buffer */
/* ncopy use this call */
int volume = (int) *(p+1);
/* (p+2) == 0xe4 or 0xe2 sometimes ???? */
int result=fill_namespace_buffer(
volume, responsedata);
if (result > -1) {
data_len = result;
} else completition = (uint8) -result;
}
break;
case 0x30 : { /* Get Name Space Directory Entry */
int volume = (int) *(p+1);
uint32 basehandle = GET_32(p+2);
int namespace = (int) *(p+6);
int result=get_namespace_dir_entry(
volume, basehandle, namespace,
responsedata);
if (result > -1) {
data_len = result;
} else completition = (uint8) -result;
}
break;
#endif
case 0x32 : { /* Get Object Effective Rights */
/*
* Client32/NWCalls export:
* NWNCP22s50GetObjEffectRights
*
* Observed request layout from Client32/NCPWIN32:
* byte subfunction 0x32
* dword object id (big endian)
* byte directory handle
* byte path length
* byte[] path
*
* Reply:
* word effective rights mask, low/high.
*
* mars_nwe's trustee engine currently computes effective
* rights for the active connection object. For the normal
* NWGetEffectiveRights/NWGetObjectEffectiveRights use from
* a logged-in client this object id is the current user.
*/
struct XDATA {
uint8 eff_rights[2]; /* LO-HI */
} *xdata = (struct XDATA*) responsedata;
uint32 object_id = GET_BE32(p+1);
int dir_handle = (int)*(p+5);
int pathlen = (int)*(p+6);
uint8 *path = p+7;
int result;
if (object_id && object_id != (uint32)act_obj_id) {
int save_act_obj_id = act_obj_id;
/*
* Best effort for explicit-object callers. Group
* membership is still the connection's group list, which
* is correct for the usual current-user call and avoids
* changing bindery/group state in this low-level handler.
*/
act_obj_id = (int)object_id;
result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
act_obj_id = save_act_obj_id;
} else {
result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1);
}
if (result > -1) {
int ncp22_rights = trustee_v3_to_ncp22_rights(result);
U16_TO_16(ncp22_rights, xdata->eff_rights);
data_len = sizeof(struct XDATA);
} else completition = (uint8)(-result);
}
break;
case 0x33 : { /* Get Extended Volume Information */
/*
* NCP 0x2222/22/51 Get Extended Volume Information
*
* WebSDK / nwvol.h context:
* Request:
* byte VolumeNumber
* Reply:
* word VolInfoReplyLen (Lo-Hi)
* NWVolExtendedInfo VolumeInfo (VolInfoReplyLen bytes)
* byte VolNameLen
* byte VolumeName[VolNameLen]
*
* NWVolExtendedInfo contains 33 little-endian 32-bit fields:
* volume type, status flags, sector/cluster geometry, total and
* free cluster counts, suballocation/limbo/compression/migration
* counters, directory-entry counters, EA counters, a Directory
* Services object id, and the last-modified date/time.
*
* MARS-NWE maps the Unix filesystem data we already expose via
* the older volume-information calls to the core geometry and
* capacity fields. NetWare-specific suballocation, limbo,
* compression, migration, EA, and DS fields are reported as zero.
*/
struct XDATA {
uint8 vol_info_reply_len[2];
uint8 vol_type[4];
uint8 status_flag[4];
uint8 sector_size[4];
uint8 sectors_per_cluster[4];
uint8 vol_size_in_clusters[4];
uint8 free_clusters[4];
uint8 sub_alloc_freeable_clusters[4];
uint8 freeable_limbo_sectors[4];
uint8 nonfreeable_limbo_sectors[4];
uint8 avail_sub_alloc_sectors[4];
uint8 nonuseable_sub_alloc_sectors[4];
uint8 sub_alloc_clusters[4];
uint8 num_data_streams[4];
uint8 num_limbo_data_streams[4];
uint8 oldest_del_file_age_in_ticks[4];
uint8 num_compressed_data_streams[4];
uint8 num_compressed_limbo_data_streams[4];
uint8 num_noncompressible_data_streams[4];
uint8 precompressed_sectors[4];
uint8 compressed_sectors[4];
uint8 num_migrated_data_streams[4];
uint8 migrated_sectors[4];
uint8 clusters_used_by_fat[4];
uint8 clusters_used_by_dirs[4];
uint8 clusters_used_by_ext_dirs[4];
uint8 total_dir_entries[4];
uint8 unused_dir_entries[4];
uint8 total_ext_dir_extants[4];
uint8 unused_ext_dir_extants[4];
uint8 ext_attrs_defined[4];
uint8 ext_attr_extants_used[4];
uint8 directory_services_object_id[4];
uint8 vol_last_modified_date_and_time[4];
uint8 vol_name_len;
uint8 vol_name[16];
} *xdata = (struct XDATA*) responsedata;
uint8 name[100];
int volume = (int) *(p+1);
int result = nw_get_volume_name(volume, name, sizeof(name));
memset(xdata, 0, sizeof(struct XDATA));
if (result > -1) {
struct fs_usage fsp;
int namlen = strlen((char*)name);
if (namlen > 16) namlen = 16;
U16_TO_16(33 * 4, xdata->vol_info_reply_len);
U32_TO_32(3, xdata->vol_type); /* VINetWare386v31 */
U32_TO_32(512, xdata->sector_size);
U32_TO_32(8, xdata->sectors_per_cluster); /* 4 KiB clusters */
if (!nw_get_fs_usage(name, &fsp, 0)) {
U32_TO_32(fsp.fsu_blocks/8, xdata->vol_size_in_clusters);
U32_TO_32(fsp.fsu_bavail/8, xdata->free_clusters);
U32_TO_32(fsp.fsu_files, xdata->total_dir_entries);
U32_TO_32(fsp.fsu_ffree, xdata->unused_dir_entries);
}
xdata->vol_name_len = namlen;
strmaxcpy(xdata->vol_name, name, namlen);
data_len = 2 + (33 * 4) + 1 + namlen;
XDPRINTF((5,0,
"NCP22/33 GetExtendedVolumeInfo: vol=%d name=`%s` len=%d",
volume, name, data_len));
} else {
completition = (uint8) -result;
}
}
break;
default:
completition = 0xfb; /* unkwown request */
break;
} /* switch *p */
}
break;
case 0x17 : { /* FILE SERVER ENVIRONMENT */
/* uint8 len = *(requestdata+1); */
uint8 ufunc = *(requestdata+2);
uint8 *rdata = requestdata+3;
switch (ufunc) {
#if FUNC_17_02_IS_DEBUG
case 0x02 : {
/* I hope this call isn't used */
/* now missused as a debug switch :) */
struct XDATA {
uint8 nw_debug; /* old level */
} *xdata = (struct XDATA*) responsedata;
if (*rdata == NWCONN) {
xdata->nw_debug = (uint8)org_nw_debug;
nw_debug = org_nw_debug = (int) *(rdata+1);
data_len = 1;
} else return(-1);
}
break;
#endif
case 0x14: /* Login Objekt, unencrypted passwords */
case 0x18: /* crypt_keyed LOGIN */
return(-2); /* nwbind must do prehandling */
case 0x0f: { /* Scan File Information */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0, len + ufunc */
uint8 sequence[2]; /* z.B. 0xff, 0xff */
uint8 dir_handle;
uint8 search_attrib; /* 0: NONE */
/* 02: HIDDEN */
/* 04: SYSTEM */
/* 06: BOTH */
/* 0x10: DIR */
uint8 len;
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 sequence[2]; /* next sequence */
/* NW_FILE_INFO f; */
uint8 f[sizeof(NW_FILE_INFO)];
uint8 owner_id[4];
uint8 archive_date[2];
uint8 archive_time[2];
uint8 reserved[56];
} *xdata = (struct XDATA*)responsedata;
int len = input->len;
int searchsequence;
NW_FILE_INFO f;
uint32 owner;
memset(xdata, 0, sizeof(struct XDATA));
searchsequence = nw_search( (uint8*) &f,
&owner,
(int)input->dir_handle,
(int) GET_BE16(input->sequence),
(int) input->search_attrib & ~0x10,
/* this routine is only for scanning files ^^^^^^ */
input->data, len);
if (searchsequence > -1) {
memcpy(xdata->f, &f, sizeof(NW_FILE_INFO));
U16_TO_BE16((uint16) searchsequence, xdata->sequence);
U32_TO_BE32(owner, xdata->owner_id);
data_len = sizeof(struct XDATA);
} else completition = (uint8) (- searchsequence);
}
break;
case 0x10: { /* Set File Information */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0, len + ufunc */
uint8 f[sizeof(NW_FILE_INFO) - 14]; /* no name */
uint8 owner_id[4];
uint8 archive_date[2];
uint8 archive_time[2];
uint8 reserved[56];
uint8 dir_handle;
uint8 search_attrib; /* 0: NONE */
/* 02: HIDDEN */
/* 04: SYSTEM */
/* 06: BOTH */
/* 0x10: DIR */
uint8 len;
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
NW_FILE_INFO f;
int result;
memcpy(((uint8*)&f)+14, input->f, sizeof(NW_FILE_INFO)-14);
result = nw_set_file_information((int)input->dir_handle,
input->data,
(int)input->len,
(int)input->search_attrib, &f);
/* no reply packet */
if (result <0) completition = (uint8)-result;
}
break;
case 0x47 : { /* SCAN BINDERY OBJECT TRUSTEE PATH */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 div[3]; /* 0x0, dlen, ufunc */
uint8 volume;
uint8 sequence[2]; /* trustee searchsequence */
uint8 id[4]; /* Trustee Object ID */
} *input = (struct INPUT *) (ncprequest);
struct XDATA {
uint8 nextsequence[2];
uint8 id[4];
uint8 access_mask;
uint8 pathlen;
uint8 path[1];
} *xdata = (struct XDATA*) responsedata;
int sequence=GET_BE16(input->sequence);
int access_mask=0;
uint32 id=GET_BE32(input->id);
int result=nw_scan_user_trustee(
input->volume, &sequence, id, &access_mask, xdata->path);
if (result > 0) {
U16_TO_BE16(sequence, xdata->nextsequence);
memcpy(xdata->id, input->id, 4);
xdata->access_mask=(uint8)access_mask;
xdata->pathlen=result;
data_len = 8+result;
} else if (!result) {
memset(xdata, 0, 8);
data_len = 8;
} else
completition = (uint8)(-result);
}
break;
case 0x64: { /* create queue */
#if 0
int q_typ = GET_BE16(rdata);
#endif
int q_name_len = *(rdata+2);
#if 0
uint8 *q_name = rdata+3;
#endif
uint8 *dirhandle = rdata+3+q_name_len;
int pathlen = *(rdata+3+q_name_len+1);
uint8 *path = rdata+3+q_name_len+2;
uint8 new_path[257];
int result = conn_get_full_path(*dirhandle,
path, pathlen, new_path,
sizeof(new_path));
if (result > -1) {
int diffsize = result - pathlen;
*dirhandle = 0;
memcpy(path, new_path, result);
if (diffsize)
requestlen+=diffsize; /* !!!!!! */
return(-1); /* nwbind must do the rest */
} else
completition = (uint8)(-result);
}
break;
case 0x68: /* create queue job and file old */
case 0x79: /* create queue job and file */
return(-2); /* nwbind must do prehandling */
case 0x6C: /* Get Queue Job Entry old */
case 0x7A: { /* Read Queue Job Entry */
uint32 q_id = GET_BE32(rdata);
int job_id = GET_BE16(rdata+4);
uint32 fhandle = get_queue_job_fhandle(q_id, job_id);
U32_TO_BE32(fhandle, rdata+8);
requestlen+=6; /* !!!!!! */
}
return(-1); /* nwbind must do the rest */
case 0x69: /* close file and start queue old ?? */
case 0x7f: { /* close file and start queue */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* lo - hi */
uint8 func; /* 0x7f or 0x69 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_id[4]; /* result from creat queue */
/* if 0x69 then only first 2 byte ! */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
int job_id = (ufunc==0x69) ? GET_BE16(input->job_id)
: GET_BE16(input->job_id);
int result = close_queue_job(q_id, job_id);
if (result < 0) {
completition = (uint8)-result;
} else {
return(-2); /* nwbind must do next */
}
}
break;
case 0x71 : /* service queue job (old) */
case 0x7c : /* service queue job */
return(-2); /* nwbind must do prehandling */
case 0x72 : /* finish queue job (old) */
case 0x73 : /* abort queue job (old) */
case 0x83 : /* finish queue job */
case 0x84 : { /* abort queue job */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x7f or 0x69 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_id[4]; /* result from creat queue */
/* if 0x69 then only first 2 byte ! */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
int job_id = GET_BE16(input->job_id);
int result = finish_abort_queue_job(q_id, job_id);
if (result <0)
completition=(uint8) -result;
else
return(-1); /* nwbind must do the rest */
}
break;
case 0xf3: { /* Map Directory Number TO PATH */
int payload_len = requestlen - 3;
int result;
if (payload_len < 0) payload_len = 0;
ncp23_debug_dump("NCP23/F3 Map Directory Number TO PATH", rdata, payload_len);
/*
* Request: volume byte, directory number dword LO-HI, namespace byte.
* Reply: length-preceded path components, no volume component.
*/
if (payload_len >= 6) {
int volume = (int)rdata[0];
uint32 dirnum = GET_32(rdata+1);
int namspace = (int)rdata[5];
result = map_directory_number_to_path(volume, dirnum, namspace,
responsedata,
sizeof(IPX_DATA) - sizeof(NCPRESPONSE));
if (result > -1) data_len = result;
else completition = (uint8)(-result);
} else {
completition = 0x9c;
}
}
break;
case 0xf4: { /* Map PATH TO Dir Entry */
int payload_len = requestlen - 3;
int result;
if (payload_len < 0) payload_len = 0;
ncp23_debug_dump("NCP23/F4 Map PATH TO Dir Entry", rdata, payload_len);
/*
* Request: dir handle byte, path length byte, path bytes.
* Reply: volume byte, directory number dword LO-HI.
*/
if (payload_len >= 2 && payload_len >= 2 + (int)rdata[1]) {
struct XDATA {
uint8 volume;
uint8 dirnum[4];
} *xdata = (struct XDATA*)responsedata;
result = conn_map_path_to_dir_entry((int)rdata[0],
rdata+2, (int)rdata[1],
&(xdata->volume),
xdata->dirnum);
if (result) completition = (uint8)(-result);
else data_len = sizeof(struct XDATA);
} else {
completition = 0x9c;
}
}
break;
default : return(-1);
break;
} /* switch (ufunc) */
} /* case 0x17 */
break;
case 0x18 : /* End of Job */
if (!(entry8_flags&0x200)) /* pcz: 14-Apr-00 */
free_connection_task_jobs(ncprequest->task);
nw_free_handles(ncprequest->task);
return(-1); /* nwbind must do a little rest */
break;
case 0x19 : /* logout, some of this call is handled in ncpserv. */
free_queue_jobs();
nw_free_handles(-1);
set_nw_user(-1, -1,
0,
0, NULL,
-1, NULL,
0, NULL);
return(-1); /* nwbind must do a little rest */
break;
case 0x1a : /* Log Physical Record */
case 0x1c : /* Release Physical Record */
case 0x1e : /* Clear Physical Record */
{
/*
* SDK: NCP 0x2222/26 Log Physical Record records one
* byte range in the caller's logged data block table.
* Lock Flag 0 logs for future locking, 1 locks
* exclusive, and 3 locks shareable/read-only.
*
* SDK: NCP 0x2222/28 Release Physical Record releases
* one locked byte range but leaves it in the logged
* table so a later Lock Physical Record Set can relock
* it. The request carries a reserved byte, a 6-byte
* NetWare file handle, Start Offset, and Record Length;
* it returns no reply data and reports Unlock Error
* through the completion code.
*
* SDK: NCP 0x2222/30 Clear Physical Record releases the
* range if it is locked and removes it from the logged
* table.
*
* The physical-record set table is maintained alongside
* the existing low-level byte-range lock implementation:
* log/lock records are added on successful 0x1a calls,
* release leaves the set entry intact, and clear removes
* the matching set entry after a successful unlock.
*/
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 lock_flag; /* 0=log/reserved, 1=excl */
/* 3=shared */
uint8 ext_fhandle[2]; /* all zero / high handle */
uint8 fhandle[4]; /* Filehandle */
uint8 offset[4];
uint8 size[4];
uint8 timeout[2];
} *input = (struct INPUT *)ncprequest;
int fhandle = GET_32 (input->fhandle);
uint32 offset= GET_BE32(input->offset);
uint32 size = GET_BE32(input->size);
uint16 timeout = GET_BE16(input->timeout);
int result;
if (function == 0x1a) { /* log or lock */
result = nw_log_physical_record(
fhandle,
offset,
size,
timeout,
(int)input->lock_flag);
if (!result)
result = share_set_physrec_add_rm(
(int)input->lock_flag,
fhandle,
offset,
size,
timeout);
} else {
result = nw_log_physical_record(
fhandle,
offset,
size,
timeout,
(function == 0x1c)
? -1 /* unlock only */
: -2 /* unlock + unlog */
);
if (!result && function == 0x1e)
result = share_set_physrec_add_rm(
-2,
fhandle,
offset,
size,
timeout);
}
if (result) completition = (uint8)-result;
}
break;
case 0x1b : /* Lock Physical Record Set (old) */
case 0x6e : { /* Lock Physical Record Set */
/*
* SDK: NCP 0x2222/27 Lock Physical Record Set (old)
* and NCP 0x2222/110 Lock Physical Record Set lock all
* byte ranges logged by the calling client.
*
* SDK request: one-byte Lock Flag followed by a 2-byte
* Lock Timeout in 1/18 second units. Lock Flag bit 1
* selects shareable/read-only locks; otherwise the set is
* locked exclusive/read-write. The reply carries no data
* and completion reports success, timeout, or lock error.
*
* MARS-NWE stores physical-record set entries from Log
* Physical Record and routes the set lock through the
* shared synchronization-set handler.
*/
struct INPUT {
uint8 header[7];
uint8 lock_flag;
uint8 timeout[2];
} *input = (struct INPUT *)ncprequest;
int lock_flag = (input->lock_flag & 0x02) ? 3 : 1;
int result = share_handle_lock_sets(
4, /* Physical Record Set */
lock_flag,
GET_BE16(input->timeout));
if (result) completition = (uint8)-result;
}
break;
case 0x1d : /* Release Physical Record Set */
case 0x1f : { /* Clear Physical Record Set */
/*
* SDK: NCP 0x2222/29 Release Physical Record Set
* releases all byte ranges locked by the calling client
* but leaves them in the client's data byte range table
* for future Lock Physical Record Set calls.
*
* SDK: NCP 0x2222/31 Clear Physical Record Set releases
* all locked ranges and clears the caller's data byte
* range table. Both calls have no reply data; Clear
* carries a one-byte Lock Flags field documented as
* 0 = Not Locked.
*/
int result = share_handle_lock_sets(
4, /* Physical Record Set */
(function==0x1f)
? -2 /* Clear */
: -1, /* Release */
0);
if (result) completition = (uint8)-result;
}
break;
case 0x20 : /* Semaphore */
return(-1); /* handled by nwbind */
case 0x21 : { /* Negotiate Buffer Size, Packetsize */
uint8 *getsize=responsedata;
int buffer_size = (int) (GET_BE16((uint8*)requestdata));
/* Der Novell-Client der PAM's Net/E-Ethernetkarte
f<>r Atari ST/TT meldet ein Packetsize von 0 wenn
nwserv NACH dem Novell Client NET_S1.PRG
gestartet wird. Da 0 in jedem Falle ein unsinniger
Wert ist, wird rw_buffer_size nicht verwendet.
Hayo Schmidt <100305.1424@compuserve.com>, 7-Dec-97
*/
if (buffer_size >= 512) {
rw_buffer_size = min(LOC_RW_BUFFERSIZE, buffer_size);
XDPRINTF((3,0, "Negotiate Buffer size = 0x%04x,(%d)",
(int) rw_buffer_size, (int) rw_buffer_size));
} else {
XDPRINTF((1,0, "Invalid Packetsize = %d, "
"Negotiate Buffer Size is set to %d",
buffer_size, rw_buffer_size));
}
U16_TO_BE16(rw_buffer_size, getsize);
data_len = 2;
}
break;
case 0x22 : { /* Transaction Tracking System (TTS) calls */
int ufunc = (int) *requestdata;
/*
* WebSDK / headers:
* NCP 0x2222/34/00 TTS Is Available has no reply data.
* Its completion code is the status value: 0x00 means
* Transaction Tracking Unavailable, 0xfd means Disabled,
* and 0xff means Available. The SDK headers expose this
* as NWTTSIsAvailable().
*
* MARS-NWE does not implement transaction tracking. Report
* the documented unavailable status for the availability
* probe, but keep the state-changing TTS subfunctions
* unsupported rather than pretending to begin, end, or abort
* transactions without rollback semantics.
*
* Cross-check: lwared and the Rust nwserver code do not
* provide a fuller TTS transaction implementation to mirror.
*/
if (!ufunc) completition=0; /* TTS unavailable */
else completition=0xfb; /* request not known */
} break;
case 0x23 : { /* div AFP Calls */
int afp_off = afp_request_offset();
uint8 *afp_req = requestdata + afp_off;
int afp_len = requestlen - afp_off;
int ufunc = (afp_len > 0) ? (int)*afp_req : -1;
/*
* NetWare AFP calls are server-side NCP entry points for
* Mac namespace semantics: AFP entry IDs, Finder Info,
* AppleDouble metadata, resource forks, and per-volume Mac
* namespace state.
*
* WebSDK / headers identify the old NCP 0x2222/35 AFP
* subfunctions used by nwafp.h, including AFP Delete
* (0x03), Get Entry ID From Name (0x04), Get File
* Information (0x05), Get Entry ID From
* NetWare Handle (0x06), Rename (0x07), Open File Fork
* (0x08), Alloc Temporary Dir Handle (0x0b), Get Entry ID
* From Path Name (0x0c), the AFP 2.0 create calls
* (0x0d/0x0e), Get/Set File Information (0x0f/0x10), and
* Scan File Information (0x11).
*
* Implement the path-name entry-id probe first because the
* SDK helpers use it to test AFP support, then expose the
* read-only AFP Get File Information query for the same
* SYS:-style path inputs. Both still require
* the optional libatalk backend to be present; without a Mac
* namespace backend, keep returning invalid namespace.
*/
if (ufunc == 0x0c) {
int result = afp_get_entry_id_from_path_name(afp_req,
afp_len, responsedata);
if (result > -1) data_len = result;
else completition = (uint8)-result;
} else if (ufunc == 0x05) {
int result = afp_get_file_information(afp_req,
afp_len, responsedata);
if (result > -1) data_len = result;
else completition = (uint8)-result;
} else {
XDPRINTF((3,0, "AFP call rejected: ufunc=0x%02x (%s), Mac namespace unavailable, libatalk backend=%s",
ufunc, afp_call_name(ufunc),
nwatalk_backend_available() ? "enabled" : "disabled"));
completition=0xbf; /* we say invalid namespace here */
}
} break;
case 0x3b : /* commit file to disk */
case 0x3d : /* commit file */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 reserve;
uint8 ext_fhandle[2]; /* all zero */
uint8 fhandle[4]; /* filehandle */
} *input = (struct INPUT *)ncprequest;
uint32 fhandle = GET_32(input->fhandle);
int result=nw_commit_file(fhandle);
if (result<0)
completition=(uint8)-result;
}
break;
case 0x3e : { /* FILE SEARCH INIT */
/* returns dhandle for searchings */
int dir_handle = (int)*requestdata;
int len = (int)*(requestdata+1); /* pathlen */
uint8 *p = requestdata+2; /* path */
struct XDATA {
uint8 volume; /* Volume */
uint8 dir_id[2]; /* Direktory ID */
uint8 searchsequence[2];
uint8 dir_rights; /* Rights */
} *xdata= (struct XDATA*) responsedata;
int volume;
int searchsequence;
int dir_id;
int rights = nw_open_dir_handle(dir_handle, p, len,
&volume, &dir_id, &searchsequence);
if (rights >-1) {
xdata->volume = (uint8)volume;
U16_TO_BE16((uint16)dir_id, xdata->dir_id);
U16_TO_BE16((uint16)searchsequence, xdata->searchsequence);
xdata->dir_rights = (uint8)rights;
data_len = sizeof(struct XDATA);
} else completition = (uint8) -rights;
} break;
case 0x3f : { /* file search continue */
/* Dir_id is from file search init */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 volume; /* Volume ID */
uint8 dir_id[2]; /* from File Search Init */
uint8 searchsequence[2]; /* sequence FFFF = first entry */
uint8 search_attrib; /* Attribute */
/* 0 none,
2 HIDDEN,
* 4 System ,
6 Both,
* 0x10 Dir
*/
uint8 len; /* fnname len */
uint8 data[2]; /* fnname with wildcards */
} *input = (struct INPUT *) ncprequest;
int len=input->len ; /* FN Length */
struct XDATA {
uint8 searchsequence[2]; /* same as request sequence */
uint8 dir_id[2]; /* Direktory ID */
/* is correct !! */
union {
NW_DIR_INFO d;
NW_FILE_INFO f;
} u;
} *xdata = (struct XDATA*)responsedata;
int searchsequence = nw_dir_search(
(uint8*) &(xdata->u),
(int) GET_BE16(input->dir_id),
(int) GET_BE16(input->searchsequence),
(int) input->search_attrib,
input->data, len);
if (searchsequence > -1) {
U16_TO_BE16((uint16) searchsequence, xdata->searchsequence);
memcpy(xdata->dir_id, input->dir_id, 2);
data_len = sizeof(struct XDATA);
} else completition = (uint8) (- searchsequence);
}
break;
case 0x40 : /* Search for a File */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 sequence[2]; /* z.B. 0xff, 0xff */
uint8 dir_handle; /* z.B 0x1 */
uint8 search_attrib; /* z.B. 0x6 */
uint8 len;
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 sequence[2]; /* answer sequence */
uint8 reserved[2]; /* z.B 0x0 0x0 */
union {
NW_DIR_INFO d;
NW_FILE_INFO f;
} u;
} *xdata = (struct XDATA*)responsedata;
int len = input->len;
uint8 my_sequence[2];
int searchsequence;
uint32 owner;
memcpy(my_sequence, input->sequence, 2);
searchsequence = nw_search( (uint8*) &(xdata->u),
&owner,
(int)input->dir_handle,
(int) GET_BE16(my_sequence),
(int) input->search_attrib,
input->data, len);
if (searchsequence > -1) {
U16_TO_BE16((uint16) searchsequence, xdata->sequence);
data_len = sizeof(struct XDATA);
} else completition = (uint8) (- searchsequence);
}
break;
case 0x41 : { /* open file for reading */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dirhandle; /* Dirhandle */
uint8 attrib; /* z.B. 0x6 od. 0x4e */
/* O_RDWR|TRUNC 0x6, O_RDONLY 0x6 */
uint8 len; /* namelaenge */
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 ext_fhandle[2]; /* all zero */
uint8 fhandle[4]; /* File Handle */
uint8 reserved[2]; /* reserved by novell */
NW_FILE_INFO fileinfo;
} *xdata= (struct XDATA*)responsedata;
int fhandle=nw_creat_open_file((int)input->dirhandle,
input->data, input->len,
&(xdata->fileinfo),
(int)input->attrib,
0x1, /* read access */
0,
(int)(ncprequest->task));
if (fhandle > -1){
U32_TO_32(fhandle, xdata->fhandle);
xdata->ext_fhandle[0]=0;
xdata->ext_fhandle[1]=0;
xdata->reserved[0]=0;
xdata->reserved[1]=0;
data_len = sizeof(struct XDATA);
} else completition = (uint8) (-fhandle);
}
break;
case 0x42 : /* close file */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 reserve;
uint8 ext_fhandle[2]; /* all zero */
uint8 fhandle[4]; /* filehandle */
} *input = (struct INPUT *)ncprequest;
uint32 fhandle = GET_32(input->fhandle);
completition = (uint8)(-nw_close_file(fhandle,
0, (int)(ncprequest->task)));
#if TEST_FNAME
if (!completition && fhandle == test_handle) {
do_druck++;
test_handle = -1;
}
#endif
}
break;
case 0x43 : /* creat file, overwrite if exist */
case 0x4D : /* create new file */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dirhandle;
uint8 attribute; /* creat Attribute */
uint8 len;
uint8 data[1]; /* Name */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 ext_fhandle[2];
uint8 fhandle[4]; /* Filehandle */
uint8 reserved[2]; /* reserved by NOVELL */
NW_FILE_INFO fileinfo;
} *xdata= (struct XDATA*)responsedata;
int fhandle=nw_creat_open_file(
(int)input->dirhandle,
input->data,
(int)input->len,
&(xdata->fileinfo),
(int)input->attribute,
/* 0, 0x2, mst: 26-Sep-99 */
0x13, /* pcz: 14-Nov-99 */
(function==0x43) ? 1 : 2,
(int)(ncprequest->task));
if (fhandle > -1){
data_len = sizeof(struct XDATA);
U32_TO_32 (fhandle, xdata->fhandle);
xdata->ext_fhandle[0]=0;
xdata->ext_fhandle[1]=0;
xdata->reserved[0]=0;
xdata->reserved[1]=0;
#ifdef TEST_FNAME
input->data[input->len] = '\0';
if (strstr(input->data, TEST_FNAME)){
test_handle = fhandle;
do_druck++;
}
#endif
} else completition = (uint8) (-fhandle);
}
break;
case 0x44 : /* file(s) delete */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dirhandle; /* 0x0 */
uint8 searchattributes;
/* 0 none, 2 Hidden, 4 System, 6 Both */
uint8 len;
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
int err_code = nw_delete_files((int)input->dirhandle,
(int)input->searchattributes,
input->data, (int)input->len);
if (err_code < 0)
completition = (uint8) -err_code;
}
break;
case 0x45 : /* rename file */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dir_handle;
uint8 searchattrib;
uint8 len;
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
uint8 *p = input->data+input->len; /* reserve z.B. 0x1 */
/* + 1 = len2 */
/* + 1 = data2 */
int errcode = nw_mv_files(
(int)input->searchattrib,
(int)input->dir_handle, input->data,(int)input->len,
(int)input->dir_handle, p+2, (int)*(p+1) );
if (errcode < 0) completition = (uint8) -errcode;
}
break;
case 0x46 : /* set file attributes */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 access; /* 0x80, od 0x0 */
/* 0x80 for example is shared */
uint8 dir_handle;
uint8 attrib; /* search attrib */
uint8 len;
uint8 data[2]; /* filename */
} *input = (struct INPUT *)ncprequest;
completition =
(uint8) (-nw_set_file_attributes((int)input->dir_handle,
input->data, (int)input->len,
(int)input->attrib,
(int)input->access));
}
break;
case 0x47 : /* move pointer to end of file ???? */
/* and return filesize ? */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 filler;
uint8 ext_filehandle[2]; /* all zero */
uint8 fhandle[4]; /* Dateihandle */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 size[4]; /* Position ??? */
} *xdata=(struct XDATA*)responsedata;
int fhandle = GET_32(input->fhandle);
int size = nw_seek_file(fhandle, 0);
if (size > -1) {
data_len = sizeof(struct XDATA);
U32_TO_BE32(size, xdata->size);
}
else completition = (uint8) -size;
}
break;
case 0x48 : /* read file */
{
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 filler;
uint8 ext_fhandle[2]; /* all zero */
uint8 fhandle[4]; /* filehandle */
uint8 offset[4];
uint8 max_size[2]; /* byte to readd */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 size[2]; /* read bytes */
uint8 data[2]; /* read data */
} *xdata=(struct XDATA*)responsedata;
int fhandle = GET_32 (input->fhandle);
int max_size = GET_BE16(input->max_size);
off_t offset = GET_BE32(input->offset);
int zusatz = (offset & 1) ? 1 : 0;
int size;
if (max_size > rw_buffer_size) {
XDPRINTF((1,0, "wanted read=%d byte > %d",
max_size, rw_buffer_size));
size = -0x88; /* we say wrong filehandle */
} else
size = nw_read_file(fhandle,
xdata->data+zusatz,
max_size,
offset);
if (size > -1) {
U16_TO_BE16(size, xdata->size);
data_len=size+zusatz+2;
} else completition = (uint8) -size;
}
break;
case 0x49 : { /* write file */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 filler; /* 0 Filler ?? */
uint8 ext_handle[2];
uint8 fhandle[4]; /* Dateihandle */
uint8 offset[4]; /* SEEK OFFSET */
uint8 size[2]; /* Datasize */
uint8 data[2]; /* Schreibdaten */
} *input = (struct INPUT *)ncprequest;
off_t offset = GET_BE32(input->offset);
int fhandle = GET_32 (input->fhandle);
int input_size = GET_BE16(input->size);
int size = nw_write_file(fhandle,
input->data,
input_size,
offset);
if (size < 0)
completition = (uint8) -size;
else if (size < input_size)
completition = (uint8)0xff;
}
break;
case 0x4a : { /* File SERVER COPY */
/* should be OK */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 reserved; /* Reserved by Novell */
uint8 qext_fhandle[2]; /* ext Filehandle */
uint8 qfhandle[4]; /* Quellfile */
uint8 zext_fhandle[2]; /* ext Filehandle */
uint8 zfhandle[4]; /* Zielfile */
uint8 qoffset[4]; /* SourceFile Offset */
uint8 zoffset[4]; /* DestFile Offset */
uint8 size[4]; /* copysize */
} *input = (struct INPUT *)ncprequest;
int qfhandle = GET_32 (input->qfhandle);
int zfhandle = GET_32 (input->zfhandle);
off_t qoffset = GET_BE32(input->qoffset);
off_t zoffset = GET_BE32(input->zoffset);
uint32 input_size = GET_BE32(input->size);
int size = nw_server_copy(qfhandle, qoffset,
zfhandle, zoffset,
input_size);
if (size < 0) completition = (uint8) -size;
else {
struct XDATA {
uint8 zsize[4]; /* real transfered Bytes */
} *xdata= (struct XDATA*)responsedata;
U32_TO_BE32(size, xdata->zsize);
data_len = sizeof(struct XDATA);
}
}
break;
case 0x4b : { /* set date of file, file will be closed later */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 filler;
uint8 reserve[2]; /* ext Filehandle */
uint8 fhandle[4]; /* Dateihandle */
uint8 zeit[2]; /* time */
uint8 datum[2]; /* date */
} *input = (struct INPUT *)ncprequest;
int result = nw_set_fdate_time(GET_32(input->fhandle),
input->datum, input->zeit);
if (result <0) completition = (uint8) -result;
}
break;
case 0x4c : { /* open file */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 dirhandle; /* Dirhandle */
uint8 attrib; /* z.B. 0x6 od. 0x4e */
/* O_RDWR|TRUNC 0x6, O_RDONLY 0x6 */
uint8 access; /* z.B. 0x9 od 0x11 od. 0x13 */
/* O_RDWR|TRUNC 0x13, O_RDONLY 0x11 */
/* O_RDWR|TRUNC|O_DENYNONE 0x3 */
/* 0x10 BINAERMODUS ?? */
/* 0x02 do write */
uint8 len; /* namelaenge */
uint8 data[2]; /* Name */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 ext_fhandle[2]; /* all zero */
uint8 fhandle[4]; /* Dateihandle */
uint8 reserved[2]; /* reserved by Novell */
NW_FILE_INFO fileinfo;
} *xdata= (struct XDATA*)responsedata;
int fhandle=nw_creat_open_file((int)input->dirhandle,
input->data, input->len,
&(xdata->fileinfo),
(int)input->attrib,
(int)input->access, 0,
(int)(ncprequest->task));
if (fhandle > -1){
U32_TO_32 (fhandle, xdata->fhandle);
xdata->ext_fhandle[0]=0;
xdata->ext_fhandle[1]=0;
xdata->reserved[0]=0;
xdata->reserved[1]=0;
data_len = sizeof(struct XDATA);
#ifdef TEST_FNAME
input->data[input->len] = '\0';
if (strstr(input->data, TEST_FNAME)){
test_handle = fhandle;
do_druck++;
}
#endif
} else completition = (uint8) (-fhandle);
}
break;
#if WITH_NAME_SPACE_CALLS
case 0x56 : /* some extended atrribute calls */
{
int result = handle_func_0x56(requestdata, responsedata, ncprequest->task);
if (result > -1) data_len = result;
else completition=(uint8)-result;
}
break;
case 0x57 : /* some new namespace calls */
{
int result = handle_func_0x57(requestdata, responsedata, ncprequest->task);
if (result > -1) data_len = result;
else completition=(uint8)-result;
}
break;
#endif
#ifdef _MAR_TESTS_XX
case 0x5f : { /* ????????????? UNIX Client */
/* a 4.1 Server also do not know this call */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 unknown[4]; /* 0x10, 0,0,0 */
} *input = (struct INPUT *)ncprequest;
completition = 0;
}
break;
#endif
case 0x61 :
#if ENABLE_BURSTMODE
if (server_version_flags&1) { /* enable Burstmode */
/* Negotiate Buffer Size, Packetsize new ? */
int wantsize = GET_BE16((uint8*)requestdata);
/* wantsize is here max.
* phys. packet size without MAC-header
* e.g. 1500 if ethernet
*/
int flags = (int) *(requestdata+2);
/**** flags ***********************
* CHECKSUMMING_REQUESTED 1
* SIGNATURE_REQUESTED 2
* COMPLETE_SIGNATURES_REQUESTED 4
* ENCRYPTION_REQUESTED 8
* LIP_DISABLED 0x80
**********************************/
struct XDATA {
uint8 getsize[2];
uint8 socket[2]; /* echo socket */
uint8 flags; /* zero */
} *xdata= (struct XDATA*)responsedata;
memset(xdata, 0, sizeof(*xdata));
wantsize = min(IPX_MAX_DATA+30, wantsize);
rw_buffer_size = min(RW_BUFFERSIZE, wantsize-64);
U16_TO_BE16(wantsize, xdata->getsize);
U16_TO_BE16(sock_echo, xdata->socket);
data_len = sizeof(*xdata);
XDPRINTF((2,0, "Packet Burst negotiate buffer: want=0x%04x reply=0x%04x rw_buffer=0x%04x echo_socket=0x%04x flags=0x%x",
(int) GET_BE16((uint8*)requestdata),
(int) wantsize,
(int) rw_buffer_size,
(int) sock_echo,
flags));
} else
#endif
{
XDPRINTF((2,0, "Function '0x61' (Burst) not enabled"));
completition = 0xfb; /* unknown request */
nw_debug=0;
}
break;
case 0x65 : /* Packet Burst Connection Request */
#if ENABLE_BURSTMODE
if (server_version_flags&1) { /* enable burstmode */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 connid[4]; /* RANDOM ID */
/* build by time() */
uint8 max_packet_size[4]; /* HI-LO */
/* max_packet_size is here max.
* phys. packet size without MAC-header
* e.g. 1500 if ethernet
*/
uint8 target_socket[2]; /* HI-LO */
uint8 max_send_size[4]; /* HI-LO */
uint8 max_recv_size[4]; /* HI-LO */
} *input = (struct INPUT *)ncprequest;
struct XDATA {
uint8 server_id[4]; /* RANDOM ID */
/* build by time() */
uint8 max_packet_size[4]; /* HI-LO */
uint8 max_send_size[4]; /* HI-LO */
uint8 max_recv_size[4]; /* HI-LO */
} *xdata= (struct XDATA*) responsedata;
int client_socket=GET_BE16(input->target_socket);
uint32 max_packet_size=min(sizeof(IPX_DATA),
GET_BE32(input->max_packet_size)-30);
U32_TO_BE32(max_packet_size + 30,
xdata->max_packet_size);
if (!burst_w)
burst_w=(BURST_W*)xcmalloc(sizeof(BURST_W));
xfree(burst_w->sendburst);
xfree(burst_w->send_buf);
xfree(burst_w->recv_buf);
burst_w->max_burst_data_size=
max_packet_size-sizeof(BURSTPACKET);
burst_w->sendburst=
(BURSTPACKET*)xcmalloc(max_packet_size);
burst_w->ud.udata.buf = (char*)(burst_w->sendburst);
burst_w->sendburst->type[0]=0x77;
burst_w->sendburst->type[1]=0x77;
burst_w->sendburst->streamtyp=2; /* BIG_SEND_BURST */
U32_TO_BE32(time(NULL), burst_w->sendburst->source_conn);
U16_TO_16(act_connection, burst_w->sendburst->source_conn);
/* we need to identify it */
memcpy(xdata->server_id,
burst_w->sendburst->source_conn, 4);
memcpy(burst_w->sendburst->dest_conn,
input->connid, 4);
burst_w->max_send_size=
min(max_burst_send_size,
GET_BE32(input->max_recv_size));
burst_w->send_buf=xcmalloc(burst_w->max_send_size+8);
burst_w->max_recv_size=
min(max_burst_recv_size,
GET_BE32(input->max_send_size));
#if 1 /* MUST BE REMOVED LATER !!! */
/* we don't want fragmented receive packets */
if (burst_w->max_recv_size >
burst_w->max_burst_data_size-24)
burst_w->max_recv_size
=burst_w->max_burst_data_size-24;
#endif
burst_w->recv_buf=xcmalloc(burst_w->max_recv_size+24);
#if 1
U32_TO_BE32(0x5ff22, burst_w->sendburst->delaytime);
#endif
U32_TO_BE32(burst_w->max_recv_size, xdata->max_recv_size);
U32_TO_BE32(burst_w->max_send_size, xdata->max_send_size);
burst_w->ipx_pack_typ = PACKT_CORE;
burst_w->ud.opt.len = sizeof(uint8);
burst_w->ud.opt.maxlen = sizeof(uint8);
burst_w->ud.opt.buf = (char*)&(burst_w->ipx_pack_typ);
memcpy(&(burst_w->to_addr), &from_addr, sizeof(ipxAddr_t));
U16_TO_BE16(client_socket, burst_w->to_addr.sock);
burst_w->ud.addr.len = sizeof(ipxAddr_t);
burst_w->ud.addr.maxlen = sizeof(ipxAddr_t);
burst_w->ud.addr.buf = (char*)&(burst_w->to_addr);
data_len = sizeof(*xdata);
XDPRINTF((2,0, "Packet Burst connection accepted: client_socket=0x%04x max_packet=%u max_data=%u send=%u recv=%u",
client_socket,
(unsigned)(max_packet_size + 30),
(unsigned)burst_w->max_burst_data_size,
(unsigned)burst_w->max_send_size,
(unsigned)burst_w->max_recv_size));
} else
#endif
{
XDPRINTF((2,0, "Packet Burst Connection Request not enabled"));
nw_debug=0;
completition = 0xfb; /* unknown request */
}
break;
case 0x68 : /* NDS NCP, NDS Fragger Protokoll ?? */
XDPRINTF((2,0, "NDS Fragger Protokoll not supportet"));
nw_debug=0;
completition = 0xfb; /* unknown request */
break;
default : completition = 0xfb; /* unknown request */
break;
} /* switch function */
} else if (ncp_type == 0x1111) {
free_queue_jobs();
(void) nw_init_connect();
last_sequence = -9999;
} else {
XDPRINTF((1,0, "WRONG TYPE:0x%x", ncp_type));
completition = 0xfb;
}
if (nw_debug && (completition == 0xfb || (do_druck == 1))) { /* UNKWON FUNCTION od. TYPE */
pr_debug_request();
if (completition == 0xfb) {
int req_len = requestlen - sizeof(NCPREQUEST);
int subfunc = -1;
if (req_len > 2 && (function == 0x15 || function == 0x16 || function == 0x17))
subfunc = requestdata[2];
else if (req_len > 0 && function == 0x20)
subfunc = requestdata[0];
if (subfunc > -1)
XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x ufunc=0x%x",
ncp_type, function, subfunc));
else
XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x",
ncp_type, function));
} else if (data_len){
int j = data_len;
uint8 *p = responsedata;
XDPRINTF((0,2, "RSPONSE: len %d, DATA:", data_len));
while (j--) {
int c = *p++;
if (c > 32 && c < 127) XDPRINTF((0,3,",\'%c\'", (char) c));
else XDPRINTF((0,3,",0x%x", c));
}
XDPRINTF((0,1, NULL));
}
}
ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len);
nw_debug = org_nw_debug;
return(0);
}
static void handle_after_bind()
{
NCPREQUEST *ncprequest = (NCPREQUEST*) saved_readbuff;
uint8 *requestdata = saved_readbuff + sizeof(NCPREQUEST);
uint8 *bindresponse = readbuff + sizeof(NCPRESPONSE);
int data_len = 0;
int completition = 0;
switch (ncprequest->function) {
/* QUOTA support from: Matt Paley */
case 0x16 : {
uint8 ufunc = *(requestdata+2);
switch (ufunc) {
case 0x21: {
/* change Vol restrictions for Obj */
uint8 volnr = *(requestdata+3);
uint32 id = GET_BE32(requestdata+4);
uint32 blocks = GET_32(requestdata+8);
int gid = ((int *) bindresponse)[0];
int uid = ((int *) bindresponse)[1];
int perm = ((int *) bindresponse)[2];
int result;
XDPRINTF((5, 0,
"Change vol rest id=%x vol=%d blocks=%d gid=%d uid=%d p=%d",
(int) id, (int) volnr, (int) blocks,
(int) gid, (int) uid, (int) perm));
if (perm == 0) {
result = nw_set_vol_restrictions(volnr, uid, blocks);
} else {
result = -0x85;
}
if (result < 0) completition = (uint8)-result;
}
break;
case 0x22: {
/* Remove Vol restrictions for Obj */
uint8 volnr = *(requestdata+3);
uint32 id = GET_BE32(requestdata+4);
int gid = ((int *) bindresponse)[0];
int uid = ((int *) bindresponse)[1];
int perm = ((int *) bindresponse)[2];
int result;
XDPRINTF((1, 0, "Remove vol rest id=%x vol=%d gid=%d uid=%d p=%d",
(int) id, (int) volnr, (int) gid, (int) uid, (int) perm));
if (perm == 0) {
result = nw_set_vol_restrictions(volnr, uid, 0);
} else {
result = -0x85;
}
if (result < 0) completition = (uint8)-result;
}
break;
case 0x29: {
/* Get Vol restrictions for Obj */
uint8 volnr = *(requestdata+3);
uint32 id = GET_BE32(requestdata+4);
int gid = ((int *) bindresponse)[0];
int uid = ((int *) bindresponse)[1];
int perm = ((int *) bindresponse)[2];
uint32 quota, used;
int result;
struct XDATA {
uint8 restriction[4];
uint8 inuse[4];
} *xdata = (struct XDATA*) responsedata;
XDPRINTF((5, 0, "Get vol rest id=%x vol=%d gid=%d uid=%d p=%d",
(int) id, (int) volnr, (int) gid, (int) uid, (int) perm));
if (perm == 0) {
result = nw_get_vol_restrictions(volnr, uid, &quota, &used);
} else {
result = -0x85;
}
data_len = 8;
if (result == 0) {
U32_TO_32(quota, xdata->restriction);
U32_TO_32(used, xdata->inuse);
} else {
U32_TO_32(0x40000000, xdata->restriction);
U32_TO_32(0x0, xdata->inuse);
completition = (uint8) -result;
}
}
break;
default : completition = 0xfb;
}
break;
}
case 0x17 : { /* FILE SERVER ENVIRONMENT */
uint8 ufunc = *(requestdata+2);
switch (ufunc) {
case 0x14: /* Login Objekt, unencrypted passwords */
case 0x18: { /* crypt_keyed LOGIN */
int grpcount = * (int*)(bindresponse + 4 * sizeof(int));
uint32 *grps = (uint32*)(bindresponse + 5 * sizeof(int));
int unxloginlen = (int)*(uint8*)(grps+grpcount);
uint8 *unxloginname = (uint8*)(grps+grpcount)+1;
uint8 objname[48];
/* ncpserv have changed the structure */
if (ufunc==0x14) {
xstrmaxcpy(objname, requestdata+6, (int) *(requestdata+5));
} else if (ufunc==0x18){
xstrmaxcpy(objname, requestdata+14, (int) *(requestdata+13));
} else objname[0]='\0';
set_nw_user(*((int*)bindresponse), /* gid */
*((int*)(bindresponse+sizeof(int))), /* uid */
*((int*)(bindresponse + 2 * sizeof(int))), /* id_flags */
*((uint32*)(bindresponse + 3 * sizeof(int))), /* id */
objname, /* login name */
unxloginlen, unxloginname,
grpcount, grps);
}
break;
case 0x68: /* create queue job and file old */
case 0x79: { /* create queue job and file */
/* nwbind made prehandling */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x79 or 0x68 */
uint8 queue_id[4]; /* Queue ID */
uint8 queue_job[280]; /* oldsize is 256 */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
uint8 *qjob = bindresponse;
int result = creat_queue_job( (int) ncprequest->task,
q_id, qjob,
responsedata,
(ufunc == 0x68) );
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
}
break;
case 0x69: /* close file and start queue old ?? */
case 0x7f: { /* close file and start queue */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x7f or 0x69 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_id[4]; /* result from creat queue */
/* if 0x69 then only 2 byte ! */
} *input = (struct INPUT *) (ncprequest);
struct RINPUT {
uint8 client_area[152];
uint8 prc_len; /* len of printcommand */
uint8 prc[1]; /* printcommand */
} *rinput = (struct RINPUT *) (bindresponse);
uint32 q_id = GET_BE32(input->queue_id);
int job_id = (ufunc==0x69) ? GET_BE16(input->job_id)
: GET_BE16(input->job_id);
int result = close_queue_job2(q_id, job_id,
rinput->client_area,
rinput->prc,
rinput->prc_len);
if (result < 0) completition = (uint8)-result;
}
break;
case 0x71 : /* service queue job (old) */
case 0x7c : { /* service queue job */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x7c,0x71 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_typ[2]; /* service typ */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
uint8 *qjob = bindresponse;
int result = service_queue_job((int)ncprequest->task,
q_id, qjob,
responsedata,
ufunc==0x71);
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
}
break;
default : completition = 0xfb;
}
}
break;
default : completition = 0xfb;
} /* switch */
ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len);
}
#if ENABLE_BURSTMODE
static int send_burst(int offset, int datasize, int flags)
{
BURSTPACKET *sb=burst_w->sendburst;
U32_TO_BE32(burst_w->packet_sequence++, sb->packet_sequence);
U32_TO_BE32(offset, sb->burstoffset);
U16_TO_BE16(datasize, sb->datasize);
U16_TO_BE16(0, sb->missing);
sb->flags = (uint8)flags;
memcpy(sb+1, burst_w->send_buf+offset, datasize);
burst_w->ud.udata.len =
burst_w->ud.udata.maxlen = datasize+sizeof(BURSTPACKET);
XDPRINTF((8, 0, "Packet Burst send packet: seq=%u off=%d size=%d flags=0x%x",
(unsigned)(burst_w->packet_sequence-1), offset, datasize, flags));
if (t_sndudata(FD_NCP_OUT, &(burst_w->ud)) < 0){
if (nw_debug) t_error("t_sndudata in NWCONN !OK");
return(-1);
}
return(0);
}
#include <sys/time.h>
static void sleep_mu(int mu)
{
struct timeval t;
t.tv_sec = 0;
t.tv_usec = mu;
(void) select(1, NULL, NULL, NULL, &t);
}
static void handle_burst_response(uint32 offset, int size)
{
BURSTPACKET *sb=burst_w->sendburst;
U16_TO_BE16(burst_w->burst_sequence, sb->burst_seq);
U16_TO_BE16(burst_w->burst_sequence+1, sb->ack_seq);
U32_TO_BE32(size, sb->burstsize);
XDPRINTF((3, 0, "Packet Burst response: seq=%u offset=%u size=%d max_packet_data=%d",
(unsigned)burst_w->burst_sequence, (unsigned)offset, size,
burst_w->max_burst_data_size));
while (size) {
int sendsize=min(size, burst_w->max_burst_data_size);
int flags=0;
size-=sendsize;
if (!size) flags|=0x10; /* EndOfBurst */
send_burst(offset, sendsize, flags);
#if 0
sleep_mu(2);
#endif
offset+=sendsize;
}
}
static void handle_burst(BURSTPACKET *bp, int len)
{
if (burst_w) {
uint32 burstoffset = GET_BE32(bp->burstoffset);
int burstsequence = GET_BE16(bp->burst_seq);
int datasize = GET_BE16(bp->datasize);
XDPRINTF((6, 0, "Packet Burst recv packet: seq=%d flags=0x%x off=%u datasize=%d len=%d",
burstsequence, bp->flags, (unsigned)burstoffset, datasize, len));
if (datasize && !(bp->flags & 0x80)) {
/* copy if no System Packet */
if (datasize+burstoffset > burst_w->max_recv_size+24) {
XDPRINTF((1, 0, "recv burstpacket offs=%d+size=%d > max_recv+24=%d",
burstoffset, datasize, burst_w->max_recv_size+24));
return;
}
memcpy(burst_w->recv_buf+burstoffset, bp+1, datasize);
}
if (bp->flags & 0x10) { /* last packet, now action */
/* 0x10 = EOB flag */
struct REQ {
uint8 function[4]; /* lo-hi 1=READ, 2=WRITE */
uint8 fhandle[4]; /* from open file */
uint8 reserved1[4]; /* all zero */
uint8 reserved2[4]; /* ??? c8,0 od. c9,f0 */
uint8 file_offset[4]; /* HI-LO */
uint8 file_size [4]; /* HI-LO */
uint8 data[2]; /* only Write */
} *req=(struct REQ*)(burst_w->recv_buf);
int function=GET_32(req->function);
if (function == 1 || function == 2) { /* Read or Write */
uint32 fhandle = GET_32(req->fhandle);
uint32 foffset = GET_BE32(req->file_offset);
uint32 fsize = GET_BE32(req->file_size);
if (function == 1) { /* Read Request */
struct XDATA {
uint8 resultcode[4]; /* lo-hi ,
* 0=noerror=OK,
* 1=init-err,
* 2=IO-err,
* 3=no data
*/
uint8 readbytes[4]; /* hi-lo */
} *xdata= (struct XDATA*)burst_w->send_buf;
int zusatz = 0; /* (foffset & 1) ? 1 : 0; */
int size;
XDPRINTF((2, 0, "Packet Burst READ request: fh=0x%x off=%u size=%u seq=%d",
(unsigned)fhandle, (unsigned)foffset, (unsigned)fsize,
burstsequence));
size = nw_read_file(fhandle,
burst_w->send_buf+sizeof(struct XDATA),
fsize, foffset);
if (zusatz) {
XDPRINTF((1, 0, "foffset=%d, fsize=%d", foffset, fsize));
}
if (size > -1) {
U32_TO_32(0, xdata->resultcode);
U32_TO_BE32(size, xdata->readbytes);
XDPRINTF((2, 0, "Packet Burst READ reply: fh=0x%x off=%u requested=%u read=%d",
(unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size));
} else {
U32_TO_32(3, xdata->resultcode);
U32_TO_BE32(0, xdata->readbytes);
size=0;
}
burst_w->burst_sequence = burstsequence;
handle_burst_response(0, size+sizeof(struct XDATA));
} else { /* Write Request */
struct XDATA {
uint8 resultcode[4]; /* lo-hi ,
* 0=noerror=OK,
* 4=write error
*/
} *xdata= (struct XDATA*)burst_w->send_buf;
int size;
XDPRINTF((2, 0, "Packet Burst WRITE request: fh=0x%x off=%u size=%u seq=%d",
(unsigned)fhandle, (unsigned)foffset, (unsigned)fsize,
burstsequence));
size = nw_write_file(fhandle, req->data, fsize, foffset);
U32_TO_32(size==fsize ? 0 : 4, xdata->resultcode);
XDPRINTF((2, 0, "Packet Burst WRITE reply: fh=0x%x off=%u requested=%u written=%d result=%s",
(unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size,
size==fsize ? "ok" : "error"));
burst_w->burst_sequence = burstsequence;
handle_burst_response(0, sizeof(struct XDATA));
}
} else {
XDPRINTF((1, 0, "burst unknow function=0x%x", function));
}
req->function[0]=0;
} else if (bp->flags & 0x80) { /* System Flag */
int missing=GET_BE16(bp->missing);
uint8 *p=(uint8*)(bp+1);
burst_w->burst_sequence = burstsequence;
XDPRINTF((3, 0, "Packet Burst missing-list request: seq=%d missing=%d",
burstsequence, missing));
while (missing--){
int offs=GET_BE32(p);
int size=GET_BE16(p+4);
XDPRINTF((3, 0, "Packet Burst resend range: off=%d size=%d", offs, size));
handle_burst_response(offs, size);
p+=6;
}
}
} else {
XDPRINTF((1, 0, "burst_w not allocated"));
}
}
#endif
static void close_all(void)
{
nw_exit_connect();
close(0);
close(FD_NCP_OUT);
}
static int fl_get_int=0;
/* signals
* &01 sig_quit
* &02 sig_hup
* &04 sig_usr1
* &08 sig_usr2
*/
static void sig_quit(int rsig)
{
XDPRINTF((2, 0, "Got Signal=%d", rsig));
fl_get_int |= 1;
}
static void sig_pipe(int rsig)
{
XDPRINTF((1, 0, "Got SIG_PIPE"));
signal(SIGPIPE, sig_pipe);
}
static void sig_hup(int rsig)
{
fl_get_int |= 2;
signal(SIGHUP, sig_hup);
}
static void sig_usr1(int rsig)
{
fl_get_int |= 4;
}
static void sig_usr2(int rsig)
{
fl_get_int |= 8;
}
static void get_new_debug(void)
{
get_ini_debug(3);
fl_get_int &= ~2;
}
static void handle_extern_command(void)
{
fl_get_int &= ~4;
signal(SIGUSR1, sig_usr1);
}
static void handle_sigusr2(void)
{
char fn[256];
FILE *f;
fl_get_int &= ~8;
sprintf(fn, "/tmp/nwconn%04d.log", act_connection);
if (seteuid(0)) {}
unlink(fn); /* security: mst:18-Apr-00 */
f=fopen(fn, "w");
(void)reseteuid();
if (f) {
log_file_module(f);
fclose(f);
} else
errorp(0, "handle_sigusr2", "cannot open %s for writing", fn);
signal(SIGUSR2, sig_usr2);
}
static void set_sig(void)
{
signal(SIGTERM, sig_quit);
signal(SIGQUIT, sig_quit);
signal(SIGINT, sig_quit);
signal(SIGPIPE, sig_pipe);
signal(SIGHUP, sig_hup);
signal(SIGUSR1, sig_usr1);
signal(SIGUSR2, sig_usr2);
if (use_mmap)
signal(SIGBUS, sig_bus_mmap); /* in nwfile.c */
}
#include <sys/resource.h>
int main(int argc, char **argv)
{
#if !CALL_NWCONN_OVER_SOCKET
int shm_id;
#endif
time_t last_time=time(NULL);
#if CALL_NWCONN_OVER_SOCKET
if (argc != 4 || 3!=sscanf(argv[3], "()INIT-:%x,%x,%x-",
&father_pid, &sock_nwbind, &sock_echo)) {
fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock-\n");
exit(1);
}
#else
if (argc != 4 || 4!=sscanf(argv[3], "()INIT-:%x,%x,%x,%x-",
&father_pid, &sock_nwbind, &sock_echo, &shm_id)) {
fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock,shm_id-\n");
exit(1);
}
#endif
prog_title=argv[3];
if (setuid(0)) {}
if (setgid(0)) {}
act_connection = atoi(*(argv+1));
#if !CALL_NWCONN_OVER_SOCKET
nwconn_state = shmat(shm_id, NULL, SHM_W);
if (nwconn_state == (char *)-1) {
errorp(0, "Can't attach shared memory segment", NULL);
exit(1);
}
#endif
init_tools(NWCONN, 0);
memset(saved_readbuff, 0, sizeof(saved_readbuff));
XDPRINTF((3, 0, "FATHER PID=%d, ADDR=%s CON:%d",
father_pid, *(argv+2), act_connection));
adr_to_ipx_addr(&from_addr, *(argv+2));
if (nw_init_connect()) exit(1);
act_pid = getpid();
#ifdef LINUX
set_emu_tli();
#endif
last_sequence = -9999;
if (get_ipx_addr(&my_addr)) exit(1);
#if CALL_NWCONN_OVER_SOCKET
# if 1
# ifdef SIOCIPXNCPCONN
{
int conn = act_connection;
int result = ioctl(0, SIOCIPXNCPCONN, &conn);
XDPRINTF((3, 0, "ioctl:SIOCIPXNCPCONN result=%d", result));
}
# endif
# endif
#endif
set_default_guid();
nwconn_set_program_title(NULL);
ud.opt.len = sizeof(uint8);
ud.opt.maxlen = sizeof(uint8);
ud.opt.buf = (char*)&ipx_pack_typ;
ud.addr.len = sizeof(ipxAddr_t);
ud.addr.maxlen = sizeof(ipxAddr_t);
ud.addr.buf = (char*)&from_addr;
ud.udata.buf = (char*)&ipxdata;
U16_TO_BE16(0x3333, ncpresponse->type);
ncpresponse->task = (uint8) 1; /* allways 1 */
ncpresponse->connection = (uint8)act_connection;
ncpresponse->high_connection = (uint8)(act_connection >> 8);
set_sig();
while ( !(fl_get_int&1) ) {
int data_len;
/* We should reply 'Request Being Processed' if request arrived twice
* or more and nwconn actually busy, if nwconn is free, we are simply
* resend previous reply.
* We are set the flag in shared memory indicating what nwconn is busy
* and check it later in ncpserv. /lenz */
#if !CALL_NWCONN_OVER_SOCKET
nwconn_state[act_connection] = 0; /* nwconn is free */
#endif
data_len = read(0, readbuff, sizeof(readbuff));
#if !CALL_NWCONN_OVER_SOCKET
nwconn_state[act_connection] = 1; /* nwconn is busy */
#endif
/* this read is a pipe or a socket read,
* depending on CALL_NWCONN_OVER_SOCKET
*/
if (fl_get_int) {
if (fl_get_int & 1) break;
if (fl_get_int & 2)
get_new_debug();
if (fl_get_int & 4)
handle_extern_command();
if (fl_get_int & 8)
handle_sigusr2();
}
if (data_len > 0) {
XDPRINTF((99, 0, "NWCONN GOT DATA len = %d",data_len));
ncpresponse->connect_status = (uint8) 0;
ncpresponse->task = ncprequest->task;
if ((ncp_type = (int)GET_BE16(ncprequest->type)) == 0x3333) {
/* this is a response packet */
data_len -= sizeof(NCPRESPONSE);
if (saved_sequence > -1
&& ((int)(ncprequest->sequence) == saved_sequence)
&& !ncprequest->function) {
/* comes from nwbind */
handle_after_bind();
} else {
/* OK for direct sending */
XDPRINTF((6,0, "NWCONN:direct sending:type 0x3333, completition=0x%x, len=%d",
(int)(ncprequest->function), data_len));
if (data_len)
memcpy(responsedata, readbuff+sizeof(NCPRESPONSE), data_len);
ncpresponse->connect_status = ((NCPRESPONSE*)readbuff)->connect_status;
ncp_response(ncprequest->sequence,
ncprequest->task,
ncprequest->function, data_len);
}
saved_sequence = -1;
} else { /* this calls I must handle, it is a request */
act_time=time(NULL);
act_ncpsequence=(int)(ncprequest->sequence);
if (act_time > last_time+60 && saved_sequence == -1) {
/* ca. 0.5 min. reset wdogs, 5 min as in original is too long for me. /lenz*/
call_nwbind(1);
last_time=act_time;
}
#if ENABLE_BURSTMODE
if (ncp_type == 0x7777) { /* BURST-MODE */
XDPRINTF((2, 0, "Packet Burst data packet received: len=%d", data_len));
handle_burst((BURSTPACKET*)readbuff, data_len);
} else
#endif
if (ncp_type == 0x2121) { /* request from nwbind */
/* mst:25-Apr-00 */
requestlen = data_len - sizeof(NCPREQUEST);
handle_nwbind_request();
} else {
int result;
requestlen = data_len - sizeof(NCPREQUEST);
if (0 != (result = handle_ncp_serv()) ) {
if (result == -2) {
/* here the actual call must be saved
* because we need it later, when the request to nwbind
* returns.
*/
memcpy(saved_readbuff, readbuff, data_len);
saved_sequence = (int)(ncprequest->sequence);
} else saved_sequence = -1;
/* this call must go to nwbind */
call_nwbind(0);
}
}
}
}
} /* while */
if (seteuid(0)) {}
# ifdef SIOCIPXNCPCONN
{
int conn = -act_connection;
(void)ioctl(0, SIOCIPXNCPCONN, &conn);
}
# endif
close_all();
XDPRINTF((3,0, "leave nwconn pid=%d", getpid()));
return(0);
}