7194 lines
296 KiB
C
7194 lines
296 KiB
C
/* 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>
|
||
#include <utime.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"
|
||
#include "nwattrib.h"
|
||
#include "nwarchive.h"
|
||
#include "namedos.h"
|
||
#include "nwsalvage.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, "DBUG NCP REQUEST func=%d ufunc=%d seq=%03d task=%02d fn=0x%02x sub=0x%02x",
|
||
(int)ncprequest->function, ufunc,
|
||
(int)ncprequest->sequence,
|
||
(int)ncprequest->task,
|
||
(int)ncprequest->function, ufunc));
|
||
} else {
|
||
XDPRINTF((1, 0, "DBUG NCP REQUEST type=%d seq=%d task=%d func=%d type_hex=0x%x fn=0x%02x",
|
||
ncp_type,
|
||
(int)ncprequest->sequence,
|
||
(int)ncprequest->task,
|
||
(int)ncprequest->function,
|
||
ncp_type,
|
||
(int)ncprequest->function));
|
||
}
|
||
if (requestlen > 0){
|
||
int j = requestlen;
|
||
uint8 *p=requestdata;
|
||
XDPRINTF((0, 2, "DBUG NCP REQUEST_DATA 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 0x01: return("AFP Create Directory");
|
||
case 0x02: return("AFP Create File");
|
||
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 0x09: return("AFP Set File Information");
|
||
case 0x0a: return("AFP Scan File Information");
|
||
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");
|
||
case 0x12: return("AFP Get DOS Name From Entry ID");
|
||
case 0x13: return("AFP Get Macintosh Info On Deleted Files");
|
||
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 int afp_volume_from_unixname(const char *unixname)
|
||
{
|
||
int k;
|
||
|
||
if (!unixname) return(-1);
|
||
for (k = 0; k < used_nw_volumes; k++) {
|
||
int len = nw_volumes[k].unixnamlen;
|
||
if (len > 0 && !strncmp(unixname, (char *)nw_volumes[k].unixname, len))
|
||
return(k);
|
||
}
|
||
return(-1);
|
||
}
|
||
|
||
static int afp_resolve_path_volume(uint8 *path, int path_len, char *unixname, int size_unixname)
|
||
/*
|
||
* Resolve the effective NetWare volume for raw AFP smoke paths such as
|
||
* SYS:PUBLIC or HOME:LOGIN. The AFP request still carries the documented
|
||
* volume byte, but the path-backed compatibility subset must not assume that
|
||
* dir_handle 0 always means volume 0: build_path() can extract the real
|
||
* volume from the VOL: prefix, and the open/dir-handle helpers use that same
|
||
* resolver internally.
|
||
*/
|
||
{
|
||
return(conn_get_kpl_unxname(unixname, size_unixname, 0, path, path_len));
|
||
}
|
||
|
||
static int afp_namespace_path_from_entry_id(int volume, uint32 entry_id,
|
||
char *path, int path_len);
|
||
static int afp_find_dos_name_from_entry_id(int volume, uint32 entry_id,
|
||
char *path, int path_len);
|
||
static int afp_resolve_entry_id_path(int volume, uint32 entry_id,
|
||
char *entry_path, int entry_path_len,
|
||
char *unixname, int unixname_len,
|
||
int *resolved_volume, int *used_len);
|
||
|
||
static int afp_build_base_relative_path(int volume, uint32 base_entry_id,
|
||
uint8 *path, int path_len,
|
||
uint8 *out, int out_size)
|
||
/*
|
||
* Build a normal mars_nwe path string for AFP create calls. AFP carries a
|
||
* volume/base entry id plus a path-modifying string. Reuse the existing
|
||
* NetWare namespace reverse mapper for non-root base IDs, then feed the
|
||
* resulting VOL:relative path to the normal mars_nwe create wrapper.
|
||
*/
|
||
{
|
||
char parent[256];
|
||
int used = 0;
|
||
int len;
|
||
|
||
if (!out || out_size < 1 || !path || path_len < 0)
|
||
return(-0x7e);
|
||
if (volume < 0 || volume >= used_nw_volumes || !nw_volumes[volume].sysname)
|
||
return(-0x98);
|
||
|
||
/* Keep the historical raw path-backed smoke form (SYS:DIR/NAME) valid. */
|
||
for (len = 0; len < path_len && path[len] != '/' && path[len] != '\\'; len++) {
|
||
if (path[len] == ':') {
|
||
if (path_len >= out_size) return(-0x96);
|
||
memcpy(out, path, path_len);
|
||
out[path_len] = '\0';
|
||
return(path_len);
|
||
}
|
||
}
|
||
|
||
len = strlen((char *)nw_volumes[volume].sysname);
|
||
if (len + 2 >= out_size) return(-0x96);
|
||
memcpy(out, nw_volumes[volume].sysname, len);
|
||
used = len;
|
||
out[used++] = ':';
|
||
|
||
if (base_entry_id) {
|
||
int result = afp_namespace_path_from_entry_id(volume, base_entry_id,
|
||
parent, sizeof(parent));
|
||
if (result < 0) return(result);
|
||
len = strlen(parent);
|
||
if (len > 0) {
|
||
if (used + len + 1 >= out_size) return(-0x96);
|
||
memcpy(out + used, parent, len);
|
||
used += len;
|
||
if (path_len && out[used-1] != '/' && out[used-1] != '\\')
|
||
out[used++] = '/';
|
||
}
|
||
}
|
||
|
||
if (used + path_len >= out_size) return(-0x96);
|
||
memcpy(out + used, path, path_len);
|
||
used += path_len;
|
||
out[used] = '\0';
|
||
return(used);
|
||
}
|
||
|
||
|
||
|
||
static uint32 afp_namespace_entry_id(int volume, const struct stat *stb)
|
||
/*
|
||
* Use mars_nwe namespace/basehandle IDs only for directories. The existing
|
||
* reverse mapper is the right object-id -> path path for directory handles,
|
||
* but regular files can collide with directory-number mappings on DOS
|
||
* namespace volumes. File AFP entry IDs therefore stay on nwatalk/fallback
|
||
* IDs until a proven file-entry reverse mapper is available.
|
||
*/
|
||
{
|
||
DEV_NAMESPACE_MAP dnm;
|
||
uint32 entry_id;
|
||
|
||
if (!stb || !S_ISDIR(stb->st_mode)) return(0);
|
||
|
||
dnm.dev = stb->st_dev;
|
||
dnm.namespace = NAME_DOS;
|
||
entry_id = nw_vol_inode_to_handle(volume, stb->st_ino, &dnm);
|
||
|
||
/* Keep the old AFP fallback range and reserve zero as invalid. */
|
||
if (!entry_id || (entry_id & 0x80000000U))
|
||
return(0);
|
||
|
||
return(entry_id);
|
||
}
|
||
|
||
static uint32 afp_fallback_entry_id(int volume, const struct stat *stb)
|
||
/*
|
||
* Build a stable local AFP entry id from Unix identity data when the NetWare
|
||
* namespace handle cannot represent the object and mars_nwe AFP xattr has no stored
|
||
* CNID/AppleDouble id yet. This is only a legacy compatibility fallback.
|
||
*/
|
||
{
|
||
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 uint32 afp_get_or_create_entry_id(const char *unixname, int volume,
|
||
const struct stat *stb,
|
||
int *fallback_out)
|
||
/*
|
||
* Return the mars_nwe namespace basehandle as AFP entry id whenever it can be
|
||
* represented. mars_nwe AFP xattr metadata remains a cache/legacy fallback for
|
||
* entries that cannot be mapped by the NetWare namespace table.
|
||
*/
|
||
{
|
||
uint32 entry_id = 0;
|
||
int result;
|
||
|
||
entry_id = afp_namespace_entry_id(volume, stb);
|
||
if (entry_id) {
|
||
if (fallback_out) *fallback_out = 0;
|
||
(void)nwatalk_set_entry_id(unixname, entry_id);
|
||
return(entry_id);
|
||
}
|
||
|
||
result = nwatalk_get_entry_id(unixname, &entry_id);
|
||
if (!result && entry_id) {
|
||
if (fallback_out) *fallback_out = 0;
|
||
return(entry_id);
|
||
}
|
||
|
||
entry_id = afp_fallback_entry_id(volume, stb);
|
||
if (fallback_out) *fallback_out = 1;
|
||
(void)nwatalk_set_entry_id(unixname, entry_id);
|
||
return(entry_id);
|
||
}
|
||
|
||
static int afp_get_entry_id_from_name(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
int path_len;
|
||
int volume;
|
||
char unixname[PATH_MAX];
|
||
struct stat stbuff;
|
||
uint32 entry_id = 0;
|
||
int result;
|
||
|
||
if (afp_len < 7) {
|
||
XDPRINTF((2,0, "WARN AFP 35/4 REJECT reason=short_request len=%d fn=0x23 sub=0x04 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
request_entry_id = GET_BE32(afp_req + 2);
|
||
path_len = (int)afp_req[6];
|
||
if (path_len < 0 || afp_len < 7 + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/4 REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x04 result=0x7e",
|
||
afp_len, path_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/4 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x04 result=0xbf"));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (!path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/4 REJECT reason=missing_path vol=%d entry=%u fn=0x23 sub=0x04 entry_hex=0x%08x result=0x9c",
|
||
(int)volume_number, request_entry_id, request_entry_id));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
if (request_entry_id) {
|
||
char base_path[256];
|
||
char entry_path[512];
|
||
int used;
|
||
|
||
result = afp_namespace_path_from_entry_id((int)volume_number,
|
||
request_entry_id,
|
||
base_path, sizeof(base_path));
|
||
if (result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/4 FAIL reason=entry_id_base_lookup vol=%d entry=%u result=0x%x fn=0x23 sub=0x04 entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id, -result, request_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
if (*base_path)
|
||
used = slprintf(entry_path, sizeof(entry_path), "%s:%s/%.*s",
|
||
nw_volumes[volume_number].sysname, base_path,
|
||
path_len, afp_req + 7);
|
||
else
|
||
used = slprintf(entry_path, sizeof(entry_path), "%s:%.*s",
|
||
nw_volumes[volume_number].sysname, path_len,
|
||
afp_req + 7);
|
||
if (used < 0 || used >= (int)sizeof(entry_path))
|
||
return(-0x96);
|
||
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
(uint8 *)entry_path, used);
|
||
if (volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/4 FAIL reason=relative_path_resolve vol=%d entry=%u base='%s' name='%s' path='%s' result=0x%x fn=0x23 sub=0x04 entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id, base_path,
|
||
visable_data(afp_req + 7, path_len), entry_path, -volume,
|
||
request_entry_id));
|
||
return(volume);
|
||
}
|
||
} else {
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
afp_req + 7, path_len);
|
||
}
|
||
if (volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/4 FAIL reason=path_resolve vol=%d entry=%u path='%s' result=0x%x fn=0x23 sub=0x04 entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), -volume, request_entry_id));
|
||
return(volume);
|
||
}
|
||
|
||
if (stat(unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/4 FAIL reason=stat vol=%d entry=%u path='%s' unix='%s' errno=%d fn=0x23 sub=0x04 entry_hex=0x%08x result=0x9c",
|
||
(int)volume_number, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), unixname, errno,
|
||
request_entry_id));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &result);
|
||
|
||
U32_TO_BE32(entry_id, response);
|
||
XDPRINTF((3,0, "INFO AFP 35/4 DONE vol=%d entry=%u path='%s' reply_entry=%u mode='%s%s' fn=0x23 sub=0x04 entry_hex=0x%08x reply_entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), entry_id,
|
||
request_entry_id ? "entry-id" : "",
|
||
result ? " fallback" : "", request_entry_id, entry_id));
|
||
return(4);
|
||
}
|
||
|
||
|
||
static int afp_get_entry_id_from_netware_handle(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
{
|
||
uint32 fhandle;
|
||
uint8 *unixname;
|
||
int volume;
|
||
struct stat stbuff;
|
||
uint32 entry_id = 0;
|
||
int result;
|
||
|
||
if (afp_len < 7) {
|
||
XDPRINTF((2,0, "WARN AFP 35/6 REJECT reason=short_request len=%d fn=0x23 sub=0x06 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
fhandle = GET_32(afp_req + 3); /* bytes 1..2 are the extended handle */
|
||
unixname = file_get_unix_name(fhandle);
|
||
if (!unixname) {
|
||
XDPRINTF((2,0, "WARN FILE 35/6 REJECT reason=bad_handle handle=%u fn=0x23 sub=0x06 result=0x88",
|
||
fhandle));
|
||
return(-0x88); /* Invalid File Handle */
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/6 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x06 result=0xbf"));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (stat((char *)unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/6 FAIL reason=stat handle=%u unix='%s' errno=%d fn=0x23 sub=0x06 result=0x88",
|
||
fhandle, unixname, errno));
|
||
return(-0x88); /* Invalid File Handle */
|
||
}
|
||
|
||
volume = afp_volume_from_unixname((char *)unixname);
|
||
if (volume < 0) volume = 0;
|
||
|
||
entry_id = afp_get_or_create_entry_id((char *)unixname, volume, &stbuff, &result);
|
||
|
||
response[0] = (uint8)volume;
|
||
U32_TO_BE32(entry_id, response + 1);
|
||
response[5] = 0; /* data fork */
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/6 DONE handle=%u vol=%d unix='%s' entry=%u mode='%s' fn=0x23 sub=0x06 entry_hex=0x%08x",
|
||
fhandle, volume, unixname, entry_id,
|
||
result ? "fallback" : "", entry_id));
|
||
return(6);
|
||
}
|
||
|
||
|
||
static int afp_open_file_fork(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
/*
|
||
* WebSDK / nwafp.h call 0x08 opens a NetWare file handle for an AFP file
|
||
* fork. AFP resource forks are still rejected until an AppleDouble/resource
|
||
* fork backend exists, but data-fork read and write opens are routed through
|
||
* the normal mars_nwe NetWare open/share path so trustee, read-only attribute,
|
||
* and share-deny semantics stay shared with regular NCP file opens.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
uint8 fork_indicator;
|
||
uint8 access_mode;
|
||
int path_len;
|
||
NW_FILE_INFO fileinfo;
|
||
int path_volume;
|
||
char unixname[PATH_MAX];
|
||
int fhandle;
|
||
uint32 fork_len = 0;
|
||
|
||
if (afp_len < 9) {
|
||
XDPRINTF((2,0, "WARN AFP 35/8 REJECT reason=short_request len=%d fn=0x23 sub=0x08 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
request_entry_id = GET_BE32(afp_req + 2);
|
||
fork_indicator = afp_req[6];
|
||
access_mode = afp_req[7];
|
||
path_len = (int)afp_req[8];
|
||
if (path_len < 0 || afp_len < 9 + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/8 REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x08 result=0x7e",
|
||
afp_len, path_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/8 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x08 result=0xbf"));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (fork_indicator != 0) {
|
||
XDPRINTF((2,0, "WARN AFP 35/8 REJECT reason=resource_fork_unsupported vol=%d entry=%u fork=%d access=0x%02x fn=0x23 sub=0x08 entry_hex=0x%08x result=0x9c",
|
||
(int)volume_number, request_entry_id, (int)fork_indicator,
|
||
(int)access_mode, request_entry_id));
|
||
return(-0x9c); /* Invalid Path until AppleDouble/resource forks exist */
|
||
}
|
||
|
||
if (!path_len) {
|
||
char entry_path[512];
|
||
int used;
|
||
int entry_result;
|
||
|
||
entry_result = afp_resolve_entry_id_path((int)volume_number,
|
||
request_entry_id,
|
||
entry_path, sizeof(entry_path),
|
||
unixname, sizeof(unixname),
|
||
&path_volume, &used);
|
||
if (entry_result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/8 FAIL reason=entry_id_lookup vol=%d entry=%u fork=%d access=0x%02x result=0x%x fn=0x23 sub=0x08 entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id, (int)fork_indicator,
|
||
(int)access_mode, -entry_result, request_entry_id));
|
||
return(entry_result);
|
||
}
|
||
|
||
memset(&fileinfo, 0, sizeof(fileinfo));
|
||
if (!(access_mode & 0x03))
|
||
access_mode |= 0x01;
|
||
|
||
fhandle = nw_creat_open_file(0, (uint8 *)entry_path, used, &fileinfo,
|
||
0, (int)(access_mode & 0x0f), 0,
|
||
(int)(ncprequest->task));
|
||
if (fhandle < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/8 FAIL reason=open request_vol=%d resolved_vol=%d entry=%u fork=%d access=0x%02x path='%s' result=0x%x fn=0x23 sub=0x08 entry_hex=0x%08x",
|
||
(int)volume_number, path_volume, request_entry_id,
|
||
(int)fork_indicator, (int)access_mode, entry_path, -fhandle, request_entry_id));
|
||
return(fhandle);
|
||
}
|
||
|
||
fork_len = GET_BE32(fileinfo.size);
|
||
response[0] = 0;
|
||
response[1] = 0;
|
||
U32_TO_32(fhandle, response + 2);
|
||
U32_TO_BE32(fork_len, response + 6);
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/8 DONE vol=%d request_vol=%d entry=%u fork=%d access=0x%02x path='%s' handle=%d fork_len=%u mode='entry-id' fn=0x23 sub=0x08 entry_hex=0x%08x fork_len_hex=0x%08x",
|
||
path_volume, (int)volume_number, request_entry_id,
|
||
(int)fork_indicator, (int)access_mode, entry_path,
|
||
fhandle, fork_len, request_entry_id, fork_len));
|
||
return(10);
|
||
}
|
||
|
||
path_volume = afp_resolve_path_volume(afp_req + 9, path_len,
|
||
unixname, sizeof(unixname));
|
||
if (path_volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/8 FAIL reason=path_resolve request_vol=%d entry=%u fork=%d access=0x%02x path='%s' result=0x%x fn=0x23 sub=0x08 entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id, (int)fork_indicator,
|
||
(int)access_mode, visable_data(afp_req + 9, path_len),
|
||
-path_volume, request_entry_id));
|
||
return(path_volume);
|
||
}
|
||
|
||
memset(&fileinfo, 0, sizeof(fileinfo));
|
||
if (!(access_mode & 0x03))
|
||
access_mode |= 0x01;
|
||
|
||
fhandle = nw_creat_open_file(0, afp_req + 9, path_len, &fileinfo,
|
||
0, (int)(access_mode & 0x0f), 0,
|
||
(int)(ncprequest->task));
|
||
if (fhandle < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/8 FAIL reason=open request_vol=%d resolved_vol=%d entry=%u fork=%d access=0x%02x path='%s' result=0x%x fn=0x23 sub=0x08 entry_hex=0x%08x",
|
||
(int)volume_number, path_volume, request_entry_id, (int)fork_indicator,
|
||
(int)access_mode, visable_data(afp_req + 9, path_len),
|
||
-fhandle, request_entry_id));
|
||
return(fhandle);
|
||
}
|
||
|
||
fork_len = GET_BE32(fileinfo.size);
|
||
response[0] = 0;
|
||
response[1] = 0;
|
||
U32_TO_32(fhandle, response + 2);
|
||
U32_TO_BE32(fork_len, response + 6);
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/8 DONE vol=%d request_vol=%d entry=%u fork=%d access=0x%02x path='%s' handle=%d fork_len=%u mode='path' fn=0x23 sub=0x08 entry_hex=0x%08x fork_len_hex=0x%08x",
|
||
path_volume, (int)volume_number, request_entry_id, (int)fork_indicator,
|
||
(int)access_mode, visable_data(afp_req + 9, path_len),
|
||
fhandle, fork_len, request_entry_id, fork_len));
|
||
return(10);
|
||
}
|
||
|
||
static int afp_alloc_temporary_dir_handle(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
/*
|
||
* WebSDK / nwafp.h call 0x0b allocates a temporary NetWare directory handle
|
||
* from AFP namespace input: volume number, AFP Entry ID, and optional AFP-style
|
||
* path. Until a persistent CNID/base-ID map exists, accept the same raw
|
||
* VOL:-style path-backed subset used by the other implemented AFP probes and
|
||
* delegate the final handle allocation to the existing NetWare directory-handle
|
||
* table.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
int path_len;
|
||
int path_volume;
|
||
char unixname[PATH_MAX];
|
||
int eff_rights = 0;
|
||
int dirhandle;
|
||
|
||
if (afp_len < 7) {
|
||
XDPRINTF((2,0, "WARN AFP 35/11 REJECT reason=short_request len=%d fn=0x23 sub=0x0b result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
request_entry_id = GET_BE32(afp_req + 2);
|
||
path_len = (int)afp_req[6];
|
||
if (path_len < 0 || afp_len < 7 + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/11 REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x0b result=0x7e",
|
||
afp_len, path_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/11 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x0b result=0xbf"));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (!path_len) {
|
||
char entry_path[512];
|
||
int entry_len;
|
||
int entry_result;
|
||
|
||
entry_result = afp_resolve_entry_id_path((int)volume_number,
|
||
request_entry_id,
|
||
entry_path, sizeof(entry_path),
|
||
unixname, sizeof(unixname),
|
||
&path_volume, &entry_len);
|
||
if (entry_result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/11 FAIL reason=entry_id_lookup vol=%d entry=%u result=0x%x fn=0x23 sub=0x0b entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id, -entry_result, request_entry_id));
|
||
return(entry_result);
|
||
}
|
||
dirhandle = nw_alloc_dir_handle(0, (uint8 *)entry_path, entry_len, 0, 1,
|
||
(int)(ncprequest->task), &eff_rights);
|
||
if (dirhandle < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/11 FAIL reason=dir_handle_alloc request_vol=%d resolved_vol=%d entry=%u path='%s' result=0x%x fn=0x23 sub=0x0b entry_hex=0x%08x",
|
||
(int)volume_number, path_volume, request_entry_id,
|
||
entry_path, -dirhandle, request_entry_id));
|
||
return(dirhandle);
|
||
}
|
||
response[0] = (uint8)dirhandle;
|
||
response[1] = (uint8)eff_rights;
|
||
XDPRINTF((3,0, "INFO AFP 35/11 DONE vol=%d request_vol=%d entry=%u path='%s' dir_handle=%d rights=%u mode='entry-id' fn=0x23 sub=0x0b entry_hex=0x%08x rights_hex=0x%03x",
|
||
path_volume, (int)volume_number, request_entry_id,
|
||
entry_path, dirhandle, eff_rights, request_entry_id, eff_rights));
|
||
return(2);
|
||
}
|
||
|
||
path_volume = afp_resolve_path_volume(afp_req + 7, path_len,
|
||
unixname, sizeof(unixname));
|
||
if (path_volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/11 FAIL reason=path_resolve request_vol=%d entry=%u path='%s' result=0x%x fn=0x23 sub=0x0b entry_hex=0x%08x",
|
||
(int)volume_number, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), -path_volume, request_entry_id));
|
||
return(path_volume);
|
||
}
|
||
|
||
dirhandle = nw_alloc_dir_handle(0, afp_req + 7, path_len, 0, 1,
|
||
(int)(ncprequest->task), &eff_rights);
|
||
if (dirhandle < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/11 FAIL reason=dir_handle_alloc request_vol=%d resolved_vol=%d entry=%u path='%s' result=0x%x fn=0x23 sub=0x0b entry_hex=0x%08x",
|
||
(int)volume_number, path_volume, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), -dirhandle, request_entry_id));
|
||
return(dirhandle);
|
||
}
|
||
|
||
response[0] = (uint8)dirhandle;
|
||
response[1] = (uint8)eff_rights;
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/11 DONE vol=%d request_vol=%d entry=%u path='%s' dir_handle=%d rights=%u mode='path' fn=0x23 sub=0x0b entry_hex=0x%08x rights_hex=0x%03x",
|
||
path_volume, (int)volume_number, request_entry_id,
|
||
visable_data(afp_req + 7, path_len), dirhandle, eff_rights, request_entry_id, eff_rights));
|
||
return(2);
|
||
}
|
||
|
||
|
||
|
||
static int afp_create_file(uint8 *afp_req, int afp_len,
|
||
uint8 *response, const char *call_name)
|
||
/*
|
||
* WebSDK / nwafp.h calls 0x02 and 0x0e create an AFP file and return the
|
||
* new AFP file ID. AFP Create File creates the file but does not leave it
|
||
* open, so route creation through the existing mars_nwe node helpers rather
|
||
* than open(2) directly. FinderInfo remains AFP-only metadata and is written
|
||
* after the file exists.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 base_entry_id;
|
||
int delete_existing;
|
||
uint8 *finder_info;
|
||
int is_afp20;
|
||
int path_len_off;
|
||
int path_off;
|
||
int path_len;
|
||
uint8 create_path[512];
|
||
char unixname[PATH_MAX];
|
||
int volume;
|
||
int result;
|
||
struct stat stbuff;
|
||
uint32 entry_id = 0;
|
||
int fallback = 0;
|
||
int sub = (afp_len > 0) ? afp_req[0] : 0;
|
||
int afp_no = (sub == 0x0e) ? 14 : 2;
|
||
|
||
(void)call_name;
|
||
if (afp_len < 40) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
is_afp20 = (afp_req[0] == 0x0e);
|
||
path_len_off = is_afp20 ? 45 : 39;
|
||
path_off = is_afp20 ? 46 : 40;
|
||
if (afp_len <= path_len_off) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
base_entry_id = GET_BE32(afp_req + 2);
|
||
delete_existing = afp_req[6] ? 1 : 0;
|
||
finder_info = afp_req + 7;
|
||
path_len = (int)afp_req[path_len_off];
|
||
if (path_len < 0 || afp_len < path_off + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, path_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/%d REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x%02x result=0xbf",
|
||
afp_no, sub));
|
||
return(-0xbf);
|
||
}
|
||
if (!path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=empty_path vol=%d base=%u fn=0x23 sub=0x%02x base_hex=0x%08x result=0x9c",
|
||
afp_no, (int)volume_number, base_entry_id, sub, base_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
result = afp_build_base_relative_path((int)volume_number, base_entry_id,
|
||
afp_req + path_off, path_len,
|
||
create_path, sizeof(create_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=path_build vol=%d base=%u path='%s' result=0x%x fn=0x23 sub=0x%02x base_hex=0x%08x",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), -result, sub, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
create_path, strlen((char *)create_path));
|
||
if (volume < 0) return(volume);
|
||
|
||
if (!stat(unixname, &stbuff)) {
|
||
if (S_ISDIR(stbuff.st_mode)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d REJECT reason=existing_directory vol=%d base=%u path='%s' unix='%s' fn=0x23 sub=0x%02x base_hex=0x%08x result=0xff",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), unixname, sub, base_entry_id));
|
||
return(-0xff);
|
||
}
|
||
if (!delete_existing) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d REJECT reason=file_exists vol=%d base=%u path='%s' unix='%s' fn=0x23 sub=0x%02x base_hex=0x%08x result=0xff",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), unixname, sub, base_entry_id));
|
||
return(-0xff);
|
||
}
|
||
|
||
result = nw_delete_files(0, 0, create_path, strlen((char *)create_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=delete_existing vol=%d base=%u path='%s' mars_path='%s' result=0x%x fn=0x23 sub=0x%02x base_hex=0x%08x",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), create_path,
|
||
-result, sub, base_entry_id));
|
||
return(result);
|
||
}
|
||
}
|
||
|
||
result = nw_creat_node(volume, (uint8 *)unixname, 0);
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=create vol=%d base=%u path='%s' mars_path='%s' unix='%s' result=0x%x fn=0x23 sub=0x%02x base_hex=0x%08x",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), create_path,
|
||
unixname, -result, sub, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
if (stat(unixname, &stbuff)) return(-0x9c);
|
||
(void)nwatalk_set_finder_info(unixname, finder_info, 32);
|
||
|
||
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &fallback);
|
||
U32_TO_BE32(entry_id, response);
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE request_vol=%d resolved_vol=%d base=%u path='%s' entry=%u mode='%s' fn=0x23 sub=0x%02x base_hex=0x%08x entry_hex=0x%08x",
|
||
afp_no, (int)volume_number, volume, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), entry_id,
|
||
fallback ? "fallback" : "", sub, base_entry_id, entry_id));
|
||
return(4);
|
||
}
|
||
|
||
|
||
static int afp_create_directory(uint8 *afp_req, int afp_len,
|
||
uint8 *response, const char *call_name)
|
||
/*
|
||
* WebSDK / nwafp.h calls 0x01 and 0x0d create an AFP directory and return
|
||
* the new AFP directory ID. Keep NetWare semantics in the existing mars_nwe
|
||
* wrappers: resolve the AFP volume/base-id/path to a normal mars_nwe path,
|
||
* call nw_mk_rd_dir(), then derive the returned ID from the NetWare namespace
|
||
* basehandle. FinderInfo remains AFP-only metadata and is written after the
|
||
* directory exists.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 base_entry_id;
|
||
uint8 *finder_info;
|
||
int is_afp20;
|
||
int path_len_off;
|
||
int path_off;
|
||
int path_len;
|
||
uint8 create_path[512];
|
||
char unixname[PATH_MAX];
|
||
int volume;
|
||
int result;
|
||
struct stat stbuff;
|
||
uint32 entry_id = 0;
|
||
int fallback = 0;
|
||
int sub = (afp_len > 0) ? afp_req[0] : 0;
|
||
int afp_no = (sub == 0x0d) ? 13 : 1;
|
||
|
||
(void)call_name;
|
||
if (afp_len < 40) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
is_afp20 = (afp_req[0] == 0x0d);
|
||
path_len_off = is_afp20 ? 45 : 39;
|
||
path_off = is_afp20 ? 46 : 40;
|
||
if (afp_len <= path_len_off) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
base_entry_id = GET_BE32(afp_req + 2);
|
||
finder_info = afp_req + 7;
|
||
path_len = (int)afp_req[path_len_off];
|
||
if (path_len < 0 || afp_len < path_off + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_no, afp_len, path_len, sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/%d REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x%02x result=0xbf",
|
||
afp_no, sub));
|
||
return(-0xbf);
|
||
}
|
||
if (!path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=empty_path vol=%d base=%u fn=0x23 sub=0x%02x base_hex=0x%08x result=0x9c",
|
||
afp_no, (int)volume_number, base_entry_id, sub, base_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
result = afp_build_base_relative_path((int)volume_number, base_entry_id,
|
||
afp_req + path_off, path_len,
|
||
create_path, sizeof(create_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=path_build vol=%d base=%u path='%s' result=0x%x fn=0x23 sub=0x%02x base_hex=0x%08x",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), -result, sub, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
result = nw_mk_rd_dir(0, create_path, strlen((char *)create_path), 1);
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=create vol=%d base=%u path='%s' mars_path='%s' result=0x%x fn=0x23 sub=0x%02x base_hex=0x%08x",
|
||
afp_no, (int)volume_number, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), create_path,
|
||
-result, sub, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
create_path, strlen((char *)create_path));
|
||
if (volume < 0) return(volume);
|
||
if (stat(unixname, &stbuff)) return(-0x9c);
|
||
|
||
(void)nwatalk_set_finder_info(unixname, finder_info, 32);
|
||
|
||
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &fallback);
|
||
U32_TO_BE32(entry_id, response);
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE request_vol=%d resolved_vol=%d base=%u path='%s' entry=%u mode='%s' fn=0x23 sub=0x%02x base_hex=0x%08x entry_hex=0x%08x",
|
||
afp_no, (int)volume_number, volume, base_entry_id,
|
||
visable_data(afp_req + path_off, path_len), entry_id,
|
||
fallback ? "fallback" : "", sub, base_entry_id, entry_id));
|
||
return(4);
|
||
}
|
||
|
||
|
||
|
||
static int afp_delete_object(uint8 *afp_req, int afp_len,
|
||
const char *call_name)
|
||
/*
|
||
* WebSDK / nwafp.h call 0x03 removes an AFP file or directory. Keep the
|
||
* delete operation on existing mars_nwe paths: resolve volume/base-id/path to
|
||
* the normal NetWare path string, then call nw_mk_rd_dir(..., mode=0) for
|
||
* directories or nw_delete_files() for files. This avoids a direct
|
||
* unlink(2)/rmdir(2) AFP side path while giving the smoke suite a server-side
|
||
* cleanup path for AFP-created objects.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 base_entry_id;
|
||
int path_len;
|
||
uint8 delete_path[512];
|
||
char unixname[PATH_MAX];
|
||
int volume;
|
||
int result;
|
||
struct stat stbuff;
|
||
|
||
(void)call_name;
|
||
if (afp_len < 7) {
|
||
XDPRINTF((2,0, "WARN AFP 35/3 REJECT reason=short_request len=%d fn=0x23 sub=0x03 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
base_entry_id = GET_BE32(afp_req + 2);
|
||
path_len = (int)afp_req[6];
|
||
if (path_len < 0 || afp_len < 7 + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/3 REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x03 result=0x7e",
|
||
afp_len, path_len));
|
||
return(-0x7e);
|
||
}
|
||
if (!path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/3 REJECT reason=empty_path vol=%d base=%u fn=0x23 sub=0x03 base_hex=0x%08x result=0x9c",
|
||
(int)volume_number, base_entry_id, base_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
result = afp_build_base_relative_path((int)volume_number, base_entry_id,
|
||
afp_req + 7, path_len,
|
||
delete_path, sizeof(delete_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/3 FAIL reason=path_build vol=%d base=%u path='%s' result=0x%x fn=0x23 sub=0x03 base_hex=0x%08x",
|
||
(int)volume_number, base_entry_id,
|
||
visable_data(afp_req + 7, path_len), -result, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
delete_path, strlen((char *)delete_path));
|
||
if (volume < 0) return(volume);
|
||
if (stat(unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/3 FAIL reason=stat vol=%d base=%u path='%s' mars_path='%s' unix='%s' errno=%d fn=0x23 sub=0x03 base_hex=0x%08x result=0x9c",
|
||
(int)volume_number, base_entry_id,
|
||
visable_data(afp_req + 7, path_len), delete_path, unixname,
|
||
errno, base_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
if (S_ISDIR(stbuff.st_mode)) {
|
||
result = nw_mk_rd_dir(0, delete_path, strlen((char *)delete_path), 0);
|
||
} else {
|
||
result = nw_delete_files(0, 0, delete_path, strlen((char *)delete_path));
|
||
}
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/3 FAIL reason=delete vol=%d base=%u path='%s' mars_path='%s' unix='%s' result=0x%x fn=0x23 sub=0x03 base_hex=0x%08x",
|
||
(int)volume_number, base_entry_id,
|
||
visable_data(afp_req + 7, path_len), delete_path, unixname,
|
||
-result, base_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/3 DONE request_vol=%d resolved_vol=%d base=%u path='%s' directory=%d fn=0x23 sub=0x03 base_hex=0x%08x",
|
||
(int)volume_number, volume, base_entry_id,
|
||
visable_data(afp_req + 7, path_len), S_ISDIR(stbuff.st_mode) ? 1 : 0, base_entry_id));
|
||
return(0);
|
||
}
|
||
|
||
|
||
|
||
static int afp_rename_object(uint8 *afp_req, int afp_len,
|
||
const char *call_name)
|
||
/*
|
||
* WebSDK / nwafp.h call 0x07 renames or moves an AFP file or directory.
|
||
* Keep the operation on existing mars_nwe paths: resolve source and
|
||
* destination volume/base-id/path tuples to normal NetWare path strings, then
|
||
* delegate to nw_mv_files() for files or nw_mv_dir_between_handles() for
|
||
* directories. Do not introduce a direct POSIX rename(2) AFP side path.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 source_entry_id;
|
||
uint32 dest_entry_id;
|
||
int source_len;
|
||
int dest_len_off;
|
||
int dest_len;
|
||
uint8 source_path[512];
|
||
uint8 dest_path[512];
|
||
char source_unixname[PATH_MAX];
|
||
int volume;
|
||
int result;
|
||
struct stat stbuff;
|
||
|
||
(void)call_name;
|
||
if (afp_len < 12) {
|
||
XDPRINTF((2,0, "WARN AFP 35/7 REJECT reason=short_request len=%d fn=0x23 sub=0x07 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
source_entry_id = GET_BE32(afp_req + 2);
|
||
dest_entry_id = GET_BE32(afp_req + 6);
|
||
source_len = (int)afp_req[10];
|
||
if (source_len < 0 || afp_len < 11 + source_len + 1) {
|
||
XDPRINTF((2,0, "WARN AFP 35/7 REJECT reason=source_boundary_check len=%d source_len=%d fn=0x23 sub=0x07 result=0x7e",
|
||
afp_len, source_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
dest_len_off = 11 + source_len;
|
||
dest_len = (int)afp_req[dest_len_off];
|
||
if (dest_len < 0 || afp_len < dest_len_off + 1 + dest_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/7 REJECT reason=dest_boundary_check len=%d dest_len=%d fn=0x23 sub=0x07 result=0x7e",
|
||
afp_len, dest_len));
|
||
return(-0x7e);
|
||
}
|
||
if (!source_len || !dest_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/7 REJECT reason=empty_source_or_dest source_len=%d dest_len=%d fn=0x23 sub=0x07 result=0x9c",
|
||
source_len, dest_len));
|
||
return(-0x9c);
|
||
}
|
||
|
||
result = afp_build_base_relative_path((int)volume_number, source_entry_id,
|
||
afp_req + 11, source_len,
|
||
source_path, sizeof(source_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/7 FAIL reason=source_path_build vol=%d base=%u path='%s' result=0x%x fn=0x23 sub=0x07 base_hex=0x%08x",
|
||
(int)volume_number, source_entry_id,
|
||
visable_data(afp_req + 11, source_len), -result, source_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
result = afp_build_base_relative_path((int)volume_number, dest_entry_id,
|
||
afp_req + dest_len_off + 1, dest_len,
|
||
dest_path, sizeof(dest_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/7 FAIL reason=dest_path_build vol=%d base=%u path='%s' result=0x%x fn=0x23 sub=0x07 base_hex=0x%08x",
|
||
(int)volume_number, dest_entry_id,
|
||
visable_data(afp_req + dest_len_off + 1, dest_len), -result, dest_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
volume = conn_get_kpl_unxname(source_unixname, sizeof(source_unixname), 0,
|
||
source_path, strlen((char *)source_path));
|
||
if (volume < 0) return(volume);
|
||
if (stat(source_unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/7 FAIL reason=source_stat vol=%d source_base=%u path='%s' mars_path='%s' unix='%s' errno=%d fn=0x23 sub=0x07 source_base_hex=0x%08x result=0x9c",
|
||
(int)volume_number, source_entry_id,
|
||
visable_data(afp_req + 11, source_len), source_path,
|
||
source_unixname, errno, source_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
if (S_ISDIR(stbuff.st_mode)) {
|
||
result = nw_mv_dir_between_handles(0, source_path, strlen((char *)source_path),
|
||
0, dest_path, strlen((char *)dest_path));
|
||
} else {
|
||
/*
|
||
* File search attributes must not include the directory bit (0x10).
|
||
* The first AFP rename smoke showed nw_mv_files() returning -0xff for
|
||
* a normal file when the source search mask was 0x37, because the
|
||
* NetWare file search path filtered regular files out whenever 0x10 was
|
||
* present. Keep hidden/system/archive matching enabled, but search for
|
||
* files only.
|
||
*/
|
||
result = nw_mv_files(0x27,
|
||
0, source_path, strlen((char *)source_path),
|
||
0, dest_path, strlen((char *)dest_path));
|
||
}
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/7 FAIL reason=rename vol=%d source_base=%u dest_base=%u source='%s' dest='%s' result=0x%x fn=0x23 sub=0x07 source_base_hex=0x%08x dest_base_hex=0x%08x",
|
||
(int)volume_number, source_entry_id, dest_entry_id,
|
||
source_path, dest_path, -result, source_entry_id, dest_entry_id));
|
||
return(result);
|
||
}
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/7 DONE request_vol=%d resolved_vol=%d source_base=%u dest_base=%u source='%s' dest='%s' directory=%d fn=0x23 sub=0x07 source_base_hex=0x%08x dest_base_hex=0x%08x",
|
||
(int)volume_number, volume, source_entry_id, dest_entry_id,
|
||
visable_data(afp_req + 11, source_len),
|
||
visable_data(afp_req + dest_len_off + 1, dest_len),
|
||
S_ISDIR(stbuff.st_mode) ? 1 : 0, source_entry_id, dest_entry_id));
|
||
return(0);
|
||
}
|
||
|
||
|
||
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, "WARN AFP 35/12 REJECT reason=short_request len=%d fn=0x23 sub=0x0c result=0x7e",
|
||
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, "WARN AFP 35/12 REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x0c result=0x7e",
|
||
afp_len, path_len));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/12 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x0c result=0xbf"));
|
||
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, "WARN MAP 35/12 FAIL reason=path_resolve dh=%d path='%s' result=0x%x fn=0x23 sub=0x0c",
|
||
(int)dir_handle, visable_data(afp_req + 3, path_len), -volume));
|
||
return(volume);
|
||
}
|
||
|
||
if (stat(unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/12 FAIL reason=stat dh=%d path='%s' unix='%s' errno=%d fn=0x23 sub=0x0c result=0x9c",
|
||
(int)dir_handle, visable_data(afp_req + 3, path_len),
|
||
unixname, errno));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &result);
|
||
|
||
U32_TO_BE32(entry_id, response);
|
||
XDPRINTF((3,0, "INFO AFP 35/12 DONE dh=%d path='%s' entry=%u mode='%s' fn=0x23 sub=0x0c entry_hex=0x%08x",
|
||
(int)dir_handle, visable_data(afp_req + 3, path_len), entry_id,
|
||
result ? "fallback" : "", entry_id));
|
||
return(4);
|
||
}
|
||
|
||
|
||
typedef struct {
|
||
uint32 target_entry_id;
|
||
char path[256];
|
||
} AFP_DOS_NAME_SEARCH;
|
||
|
||
static int afp_path_join(char *dst, int dst_len, const char *base,
|
||
const char *name)
|
||
{
|
||
int used;
|
||
|
||
if (!dst || dst_len < 1 || !base || !name) return(-1);
|
||
if (!*base)
|
||
used = slprintf(dst, dst_len, "%s", name);
|
||
else
|
||
used = slprintf(dst, dst_len, "%s/%s", base, name);
|
||
if (used < 0 || used >= dst_len) return(-1);
|
||
return(0);
|
||
}
|
||
|
||
static int afp_dos_path_join(int volume, char *dst, int dst_len,
|
||
const char *base, const char *parent_unix,
|
||
const char *real_name, ino_t inode)
|
||
/*
|
||
* Build the documented DOS namespace path component by reusing namedos.c.
|
||
*
|
||
* AFP 0x12 returns a DOSPathString, not the real Unix directory entry name.
|
||
* The reverse lookup still walks the Unix volume tree because current AFP entry
|
||
* IDs are mars_nwe AFP xattr metadata IDs, but the path returned to the client
|
||
* must be the DOS namespace alias that mars_nwe already uses elsewhere.
|
||
*/
|
||
{
|
||
uint8 alias[14];
|
||
|
||
if (volume < 0 || volume >= used_nw_volumes) return(-1);
|
||
if (!real_name || !*real_name) return(-1);
|
||
|
||
if (!build_dos_83_alias(nw_volumes[volume].options,
|
||
(uint8 *)parent_unix, (uint8 *)real_name, inode,
|
||
alias, sizeof(alias)))
|
||
return(-1);
|
||
|
||
return(afp_path_join(dst, dst_len, base, (char *)alias));
|
||
}
|
||
|
||
static int afp_find_dos_name_from_entry_id_rec(int volume,
|
||
const char *unix_dir,
|
||
const char *rel_dir,
|
||
AFP_DOS_NAME_SEARCH *search)
|
||
/*
|
||
* Reverse-map the mars_nwe AFP entry-id xattr cache back to a DOS/NetWare path.
|
||
*
|
||
* NetWare's documented AFP 0x12 call is an entry-id-only lookup. mars_nwe's
|
||
* current AFP ids are not the namespace base handles used by namspace.c; they
|
||
* are mars_nwe AFP xattr metadata ids. Reuse the existing volume table for
|
||
* the search root and the nwatalk entry-id helper for the per-entry identity,
|
||
* but do not create fallback ids while scanning. The first conservative smoke
|
||
* target is therefore an entry that was already cached by Get Entry ID/Get File
|
||
* Information/Scan.
|
||
*/
|
||
{
|
||
DIR *dir;
|
||
struct dirent *de;
|
||
char unix_child[PATH_MAX];
|
||
char rel_child[256];
|
||
|
||
(void)volume;
|
||
dir = opendir(unix_dir);
|
||
if (!dir) return(-0x89); /* No Search Privilege */
|
||
|
||
while ((de = readdir(dir)) != NULL) {
|
||
struct stat stb;
|
||
uint32 entry_id = 0;
|
||
|
||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||
continue;
|
||
if (afp_path_join(unix_child, sizeof(unix_child), unix_dir, de->d_name))
|
||
continue;
|
||
if (lstat(unix_child, &stb))
|
||
continue;
|
||
if (afp_dos_path_join(volume, rel_child, sizeof(rel_child), rel_dir,
|
||
unix_dir, de->d_name, stb.st_ino))
|
||
continue;
|
||
|
||
if (!nwatalk_get_entry_id(unix_child, &entry_id)
|
||
&& entry_id == search->target_entry_id) {
|
||
strmaxcpy((uint8 *)search->path, rel_child,
|
||
sizeof(search->path)-1);
|
||
closedir(dir);
|
||
return(0);
|
||
}
|
||
|
||
if (S_ISDIR(stb.st_mode)) {
|
||
int result = afp_find_dos_name_from_entry_id_rec(volume, unix_child,
|
||
rel_child, search);
|
||
if (!result || result != -0xff) {
|
||
closedir(dir);
|
||
return(result);
|
||
}
|
||
}
|
||
}
|
||
closedir(dir);
|
||
return(-0xff); /* Failure, no files found */
|
||
}
|
||
|
||
static int afp_namespace_path_from_entry_id(int volume, uint32 entry_id,
|
||
char *path, int path_len)
|
||
/*
|
||
* First try the existing NetWare namespace reverse mapper. It returns
|
||
* length-prefixed DOS path components; AFP 0x12 wants a single DOSPathString,
|
||
* so join those components with '/'.
|
||
*/
|
||
{
|
||
uint8 raw[512];
|
||
int raw_len;
|
||
int in = 0;
|
||
int out = 0;
|
||
|
||
if (!path || path_len < 1) return(-0xff);
|
||
*path = '\0';
|
||
|
||
raw_len = map_directory_number_to_path(volume, entry_id, NAME_DOS,
|
||
raw, sizeof(raw));
|
||
if (raw_len < 0)
|
||
return(raw_len);
|
||
if (!raw_len)
|
||
return(0);
|
||
|
||
while (in < raw_len) {
|
||
int len = raw[in++];
|
||
|
||
if (len < 0 || in + len > raw_len) return(-0xff);
|
||
if (out) {
|
||
if (out + 1 >= path_len) return(-0x96);
|
||
path[out++] = '/';
|
||
}
|
||
if (out + len >= path_len) return(-0x96);
|
||
memcpy(path + out, raw + in, len);
|
||
out += len;
|
||
in += len;
|
||
}
|
||
path[out] = '\0';
|
||
return(0);
|
||
}
|
||
|
||
static int afp_find_dos_name_from_entry_id(int volume, uint32 entry_id,
|
||
char *path, int path_len)
|
||
{
|
||
AFP_DOS_NAME_SEARCH search;
|
||
char root[PATH_MAX];
|
||
int len;
|
||
|
||
if (volume < 0 || volume >= used_nw_volumes || !nw_volumes[volume].unixname)
|
||
return(-0x98); /* Volume does not exist */
|
||
if (entry_id == 1) {
|
||
if (path && path_len > 0) *path = '\0';
|
||
return(0);
|
||
}
|
||
|
||
len = nw_volumes[volume].unixnamlen;
|
||
if (len < 1 || len >= (int)sizeof(root)) return(-0x98);
|
||
memcpy(root, nw_volumes[volume].unixname, len);
|
||
root[len] = '\0';
|
||
while (len > 1 && root[len-1] == '/')
|
||
root[--len] = '\0';
|
||
|
||
memset(&search, 0, sizeof(search));
|
||
search.target_entry_id = entry_id;
|
||
if (!afp_find_dos_name_from_entry_id_rec(volume, root, "", &search)) {
|
||
strmaxcpy((uint8 *)path, search.path, path_len-1);
|
||
return(0);
|
||
}
|
||
return(-0xff);
|
||
}
|
||
|
||
static int afp_resolve_entry_id_path(int volume, uint32 entry_id,
|
||
char *entry_path, int entry_path_len,
|
||
char *unixname, int unixname_len,
|
||
int *resolved_volume, int *used_len)
|
||
/*
|
||
* Resolve a documented AFP entry-id-only request to a normal VOL:PATH string
|
||
* and Unix path. The reverse lookup is deliberately based on mars_nwe's AFP
|
||
* xattr entry-id cache rather than treating regular file IDs as DOS namespace
|
||
* directory numbers. Callers can then reuse the normal mars_nwe wrappers for
|
||
* rights, attributes, open/share and timestamp semantics.
|
||
*/
|
||
{
|
||
char dos_path[256];
|
||
int result;
|
||
int used;
|
||
|
||
if (!entry_path || entry_path_len < 1 || !unixname || unixname_len < 1)
|
||
return(-0x7e);
|
||
if (volume < 0 || volume >= used_nw_volumes || !nw_volumes[volume].sysname)
|
||
return(-0x98);
|
||
if (!entry_id)
|
||
return(-0x9c);
|
||
|
||
result = afp_find_dos_name_from_entry_id(volume, entry_id,
|
||
dos_path, sizeof(dos_path));
|
||
if (result)
|
||
return(result);
|
||
|
||
used = slprintf(entry_path, entry_path_len, "%s:%s",
|
||
nw_volumes[volume].sysname, dos_path);
|
||
if (used < 0 || used >= entry_path_len)
|
||
return(-0x96);
|
||
|
||
result = afp_resolve_path_volume((uint8 *)entry_path, used,
|
||
unixname, unixname_len);
|
||
if (result < 0)
|
||
return(result);
|
||
|
||
if (resolved_volume) *resolved_volume = result;
|
||
if (used_len) *used_len = used;
|
||
return(0);
|
||
}
|
||
|
||
static int afp_get_dos_name_from_entry_id(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
{
|
||
uint8 volume_number;
|
||
uint32 entry_id;
|
||
char path[256];
|
||
int result;
|
||
int len;
|
||
|
||
if (afp_len < 6) {
|
||
XDPRINTF((2,0, "WARN AFP 35/18 REJECT reason=short_request len=%d fn=0x23 sub=0x12 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
entry_id = GET_BE32(afp_req + 2);
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/18 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x12 result=0xbf"));
|
||
return(-0xbf); /* Invalid Namespace */
|
||
}
|
||
|
||
result = afp_namespace_path_from_entry_id((int)volume_number, entry_id,
|
||
path, sizeof(path));
|
||
if (result) {
|
||
int legacy_result;
|
||
|
||
legacy_result = afp_find_dos_name_from_entry_id((int)volume_number,
|
||
entry_id, path,
|
||
sizeof(path));
|
||
if (legacy_result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/18 FAIL reason=entry_id_lookup vol=%d entry=%u namespace=0x%x legacy=0x%x fn=0x23 sub=0x12 entry_hex=0x%08x result=0x9c",
|
||
(int)volume_number, entry_id, -result, -legacy_result, entry_id));
|
||
return(legacy_result);
|
||
}
|
||
}
|
||
|
||
len = strlen(path);
|
||
if (len > 255) return(-0x96); /* Server out of memory / path too long */
|
||
response[0] = (uint8)len;
|
||
memcpy(response + 1, path, len);
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/18 DONE vol=%d entry=%u path='%s' fn=0x23 sub=0x12 entry_hex=0x%08x",
|
||
(int)volume_number, entry_id, path, entry_id));
|
||
return(1 + len);
|
||
}
|
||
|
||
|
||
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);
|
||
}
|
||
|
||
#define AFP_FILE_BITMAP_ATTRIBUTES 0x0100
|
||
#define AFP_FILE_BITMAP_ACCESS_DATE 0x0400
|
||
#define AFP_FILE_BITMAP_CREATE_DATE 0x0800
|
||
#define AFP_FILE_BITMAP_MODIFY_DATE 0x1000
|
||
#define AFP_FILE_BITMAP_BACKUP_DATE 0x2000
|
||
#define AFP_FILE_BITMAP_FINDER_INFO 0x4000
|
||
#define AFP_ATTR_HIDDEN 0x0200
|
||
#define AFP_ATTR_SYSTEM 0x0400
|
||
#define AFP_ATTR_SUBDIRECTORY 0x1000
|
||
#define AFP_ATTR_ARCHIVE 0x2000
|
||
#define AFP_ATTR_SETCLR 0x8000
|
||
#define AFP_ATTR_NETWARE_MASK \
|
||
(AFP_ATTR_HIDDEN | AFP_ATTR_SYSTEM | AFP_ATTR_ARCHIVE)
|
||
|
||
static uint16 afp_basic_attributes(int volume, const char *unixname, const struct stat *stb)
|
||
{
|
||
uint16 attributes = 0;
|
||
|
||
if (S_ISDIR(stb->st_mode))
|
||
attributes |= AFP_ATTR_SUBDIRECTORY;
|
||
|
||
if (!S_ISDIR(stb->st_mode)) {
|
||
uint32 nw_attrs = get_nw_attrib_dword(volume, (char *)unixname,
|
||
(struct stat *)stb);
|
||
|
||
if (nw_attrs & FILE_ATTR_H)
|
||
attributes |= AFP_ATTR_HIDDEN;
|
||
if (nw_attrs & FILE_ATTR_S)
|
||
attributes |= AFP_ATTR_SYSTEM;
|
||
if (nw_attrs & FILE_ATTR_A)
|
||
attributes |= AFP_ATTR_ARCHIVE;
|
||
}
|
||
|
||
return(attributes);
|
||
}
|
||
|
||
#define AFP_PRIV_READ 0x0100
|
||
#define AFP_PRIV_WRITE 0x0200
|
||
#define AFP_PRIV_OPEN 0x0400
|
||
#define AFP_PRIV_CREATE 0x0800
|
||
#define AFP_PRIV_DELETE 0x1000
|
||
#define AFP_PRIV_PARENTAL 0x2000
|
||
#define AFP_PRIV_SEARCH 0x4000
|
||
#define AFP_PRIV_MODIFY_ATTRS 0x8000
|
||
|
||
static uint16 afp_access_privileges(int volume, const char *unixname,
|
||
const struct stat *stb)
|
||
{
|
||
int rights;
|
||
uint16 privileges = 0;
|
||
|
||
rights = tru_get_eff_rights(volume, (uint8 *)unixname, (struct stat *)stb);
|
||
if (rights < 0)
|
||
return(0);
|
||
|
||
if (rights & TRUSTEE_S)
|
||
rights |= TRUSTEE_R | TRUSTEE_W | TRUSTEE_O | TRUSTEE_C |
|
||
TRUSTEE_E | TRUSTEE_A | TRUSTEE_F | TRUSTEE_M;
|
||
|
||
if (S_ISDIR(stb->st_mode)) {
|
||
if (rights & (TRUSTEE_C | TRUSTEE_E | TRUSTEE_M))
|
||
privileges |= AFP_PRIV_PARENTAL;
|
||
if (rights & TRUSTEE_F)
|
||
privileges |= AFP_PRIV_SEARCH;
|
||
if (rights & TRUSTEE_M)
|
||
privileges |= AFP_PRIV_MODIFY_ATTRS;
|
||
return(privileges);
|
||
}
|
||
|
||
if (rights & TRUSTEE_R)
|
||
privileges |= AFP_PRIV_READ;
|
||
if (rights & TRUSTEE_W)
|
||
privileges |= AFP_PRIV_WRITE;
|
||
if (rights & TRUSTEE_O)
|
||
privileges |= AFP_PRIV_OPEN;
|
||
if (rights & TRUSTEE_C)
|
||
privileges |= AFP_PRIV_CREATE;
|
||
if (rights & TRUSTEE_E)
|
||
privileges |= AFP_PRIV_DELETE;
|
||
if (rights & TRUSTEE_M)
|
||
privileges |= AFP_PRIV_MODIFY_ATTRS;
|
||
|
||
if (get_nw_attrib_dword(volume, (char *)unixname, (struct stat *)stb) & FILE_ATTR_R)
|
||
privileges &= ~(AFP_PRIV_WRITE | AFP_PRIV_DELETE);
|
||
|
||
return(privileges);
|
||
}
|
||
|
||
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_info_bytes_nonzero(const uint8 *data, size_t len)
|
||
{
|
||
size_t i;
|
||
|
||
if (!data) return(0);
|
||
for (i = 0; i < len; i++)
|
||
if (data[i]) return(1);
|
||
return(0);
|
||
}
|
||
|
||
static int afp_fill_file_info_response(const char *unixname,
|
||
const uint8 *display_path,
|
||
int display_path_len,
|
||
int volume,
|
||
int include_prodos_info,
|
||
uint8 *response,
|
||
uint32 *entry_id_out,
|
||
int *fallback_out)
|
||
{
|
||
struct stat stbuff;
|
||
uint32 entry_id = 0;
|
||
uint32 parent_id = 0;
|
||
uint32 resource_size = 0;
|
||
uint8 finder_info[NWATALK_FINDER_INFO_LEN];
|
||
uint8 prodos_info[NWATALK_PRODOS_INFO_LEN];
|
||
|
||
if (stat(unixname, &stbuff))
|
||
return(-0x9c); /* Invalid Path */
|
||
|
||
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, fallback_out);
|
||
|
||
memset(response, 0, include_prodos_info ? 120 : 114);
|
||
U32_TO_BE32(entry_id, response + 0);
|
||
U32_TO_BE32(parent_id, response + 4);
|
||
U16_TO_BE16(afp_basic_attributes(volume, unixname, &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);
|
||
|
||
{
|
||
uint16 create_date = 0;
|
||
uint16 create_time = 0;
|
||
uint8 fileinfo_flags = 0;
|
||
|
||
mars_nwe_get_file_info((char *)unixname, &create_date, &create_time,
|
||
NULL, &fileinfo_flags);
|
||
if (fileinfo_flags & MARS_NWE_FILEINFO_HAS_CREATE_DATE) {
|
||
U16_TO_BE16(create_date, response + 20);
|
||
} else {
|
||
un_date_2_nw(stbuff.st_ctime, response + 20, 1);
|
||
}
|
||
/* The AFP information record used by this NCP extension has no visible
|
||
* create-time slot; still persist SetInfo Create Time through nwarchive
|
||
* so classic NetWare file-info calls can return it. */
|
||
(void)create_time;
|
||
}
|
||
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);
|
||
{
|
||
uint16 archive_date = 0;
|
||
uint16 archive_time = 0;
|
||
uint8 archive_flags = 0;
|
||
|
||
mars_nwe_get_archive_info((char *)unixname, &archive_date, &archive_time,
|
||
NULL, &archive_flags);
|
||
U16_TO_BE16((archive_flags & MARS_NWE_ARCHIVE_HAS_DATE) ? archive_date : 0,
|
||
response + 28);
|
||
U16_TO_BE16((archive_flags & MARS_NWE_ARCHIVE_HAS_TIME) ? archive_time : 0,
|
||
response + 30);
|
||
}
|
||
|
||
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, display_path, display_path_len);
|
||
U32_TO_BE32(get_file_owner(&stbuff), response + 96);
|
||
afp_leaf_name_from_path(response + 100, 12, display_path, display_path_len);
|
||
U16_TO_BE16(afp_access_privileges(volume, unixname, &stbuff), response + 112);
|
||
if (include_prodos_info) {
|
||
memset(prodos_info, 0, sizeof(prodos_info));
|
||
(void)nwatalk_get_prodos_info(unixname, prodos_info, sizeof(prodos_info));
|
||
memcpy(response + 114, prodos_info, sizeof(prodos_info));
|
||
}
|
||
|
||
if (entry_id_out) *entry_id_out = entry_id;
|
||
return(include_prodos_info ? 120 : 114);
|
||
}
|
||
|
||
|
||
static int afp_get_macintosh_info_on_deleted_file(uint8 *afp_req, int afp_len,
|
||
uint8 *response)
|
||
{
|
||
uint8 volume_number;
|
||
uint32 dos_directory_number;
|
||
struct nwsalvage_scan_result scan;
|
||
uint8 finder_info[NWATALK_FINDER_INFO_LEN];
|
||
uint8 prodos_info[NWATALK_PRODOS_INFO_LEN];
|
||
const char *name;
|
||
int name_len;
|
||
int result;
|
||
|
||
if (afp_len < 6) {
|
||
XDPRINTF((2,0, "WARN AFP 35/19 REJECT reason=short_request len=%d fn=0x23 sub=0x13 result=0x7e",
|
||
afp_len));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
dos_directory_number = GET_BE32(afp_req + 2);
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/19 REJECT reason=xattr_backend_unavailable fn=0x23 sub=0x13 result=0xbf"));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
memset(&scan, 0, sizeof(scan));
|
||
result = nwsalvage_find_by_deleted_directory_number((int)volume_number,
|
||
(unsigned long)dos_directory_number,
|
||
&scan);
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN AFP 35/19 FAIL vol=%u dosdir=%lu errno=%d fn=0x23 sub=0x13 dosdir_hex=0x%08x result=0x9c",
|
||
(unsigned int)volume_number, (unsigned long)dos_directory_number,
|
||
errno, (unsigned int)dos_directory_number));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
if (result == 0) {
|
||
XDPRINTF((2,0, "INFO AFP 35/19 NOTFOUND vol=%u dosdir=%lu fn=0x23 sub=0x13 dosdir_hex=0x%08x result=0x9c",
|
||
(unsigned int)volume_number, (unsigned long)dos_directory_number,
|
||
(unsigned int)dos_directory_number));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
memset(response, 0, 51 + NWSALVAGE_NAME_MAX);
|
||
memset(finder_info, 0, sizeof(finder_info));
|
||
if (nwsalvage_metadata_finder_info(&scan.metadata, finder_info,
|
||
sizeof(finder_info)) == 0)
|
||
memcpy(response + 0, finder_info, sizeof(finder_info));
|
||
memset(prodos_info, 0, sizeof(prodos_info));
|
||
if (nwsalvage_metadata_prodos_info(&scan.metadata, prodos_info,
|
||
sizeof(prodos_info)) == 0)
|
||
memcpy(response + 32, prodos_info, sizeof(prodos_info));
|
||
U32_TO_BE32((uint32)scan.metadata.resource_fork_size, response + 38);
|
||
|
||
name = scan.metadata.original_name[0] ? scan.metadata.original_name : "";
|
||
name_len = min((int)strlen(name), NWSALVAGE_NAME_MAX - 1);
|
||
response[42] = (uint8)name_len;
|
||
memcpy(response + 43, name, name_len);
|
||
|
||
XDPRINTF((3, 0,
|
||
"INFO AFP 35/19 DONE vol=%u dosdir=%lu name=\"%s\" resource=%lu fn=0x23 sub=0x13 dosdir_hex=0x%08x resource_hex=0x%08lx prodos=%02x%02x%02x%02x%02x%02x",
|
||
(unsigned int)volume_number, (unsigned long)dos_directory_number,
|
||
name, (unsigned long)scan.metadata.resource_fork_size,
|
||
(unsigned int)dos_directory_number,
|
||
(unsigned long)scan.metadata.resource_fork_size,
|
||
prodos_info[0], prodos_info[1], prodos_info[2],
|
||
prodos_info[3], prodos_info[4], prodos_info[5]));
|
||
return(43 + name_len);
|
||
}
|
||
|
||
static int afp_get_file_information(uint8 *afp_req, int afp_len,
|
||
uint8 *response, const char *call_name)
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
uint16 request_mask;
|
||
int afp_sub = afp_req[0];
|
||
int path_len;
|
||
int volume;
|
||
char unixname[PATH_MAX];
|
||
char entry_path[512];
|
||
uint8 *path_data;
|
||
int path_data_len;
|
||
uint32 entry_id = 0;
|
||
int fallback = 0;
|
||
int result;
|
||
|
||
if (afp_len < 9) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, afp_sub));
|
||
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, "WARN AFP 35/%d REJECT reason=boundary_check len=%d path_len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, path_len, afp_sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/%d REJECT reason=no_xattr_backend fn=0x23 sub=0x%02x result=0xbf",
|
||
afp_sub, afp_sub));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (!path_len) {
|
||
result = afp_resolve_entry_id_path((int)volume_number, request_entry_id,
|
||
entry_path, sizeof(entry_path),
|
||
unixname, sizeof(unixname),
|
||
&volume, &path_data_len);
|
||
if (result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=entry_id_lookup vol=%u entry=%lu mask=%u result=0x%02x fn=0x23 sub=0x%02x entry_hex=0x%08x mask_hex=0x%04x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned int)request_mask,
|
||
(unsigned int)(-result), afp_sub,
|
||
request_entry_id, request_mask));
|
||
return(result);
|
||
}
|
||
path_data = (uint8 *)entry_path;
|
||
} else {
|
||
path_data = afp_req + 9;
|
||
path_data_len = path_len;
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
path_data, path_data_len);
|
||
if (volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=path_resolve vol=%u entry=%lu path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id,
|
||
visable_data(path_data, path_data_len), afp_sub,
|
||
request_entry_id, (unsigned int)(-volume)));
|
||
return(volume);
|
||
}
|
||
}
|
||
|
||
result = afp_fill_file_info_response(unixname, path_data, path_data_len,
|
||
volume, afp_req[0] == 0x0f,
|
||
response, &entry_id, &fallback);
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=stat vol=%u entry=%lu path='%s' unix='%s' errno=%d fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id,
|
||
visable_data(path_data, path_data_len), unixname, errno,
|
||
afp_sub, request_entry_id, (unsigned int)(-result)));
|
||
return(result);
|
||
}
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE vol=%u entry=%lu mask=%u path='%s' reply_entry=%lu mode='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x reply_entry_hex=0x%08x mask_hex=0x%04x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned int)request_mask,
|
||
visable_data(path_data, path_data_len),
|
||
(unsigned long)entry_id, fallback ? "fallback" : "",
|
||
afp_sub, request_entry_id, entry_id, request_mask));
|
||
return(result);
|
||
}
|
||
|
||
|
||
static int afp_set_netware_attributes(int volume, char *unixname,
|
||
struct stat *stb,
|
||
uint16 requested_attrs)
|
||
{
|
||
uint32 nw_attrs;
|
||
uint16 requested_bits;
|
||
uint32 mapped_bits = 0;
|
||
|
||
if (!unixname || !stb) return(-0x9c);
|
||
|
||
requested_bits = requested_attrs & ~AFP_ATTR_SETCLR;
|
||
if (requested_bits & AFP_ATTR_HIDDEN) mapped_bits |= FILE_ATTR_H;
|
||
if (requested_bits & AFP_ATTR_SYSTEM) mapped_bits |= FILE_ATTR_S;
|
||
if (requested_bits & AFP_ATTR_ARCHIVE) mapped_bits |= FILE_ATTR_A;
|
||
|
||
if (!mapped_bits) return(0);
|
||
|
||
nw_attrs = get_nw_attrib_dword(volume, unixname, stb);
|
||
if (requested_attrs & AFP_ATTR_SETCLR)
|
||
nw_attrs |= mapped_bits;
|
||
else
|
||
nw_attrs &= ~mapped_bits;
|
||
|
||
return(set_nw_attrib_word(volume, unixname, stb, (int)(nw_attrs & 0xffff)));
|
||
}
|
||
|
||
static int afp_check_metadata_modify_rights(int volume, char *unixname,
|
||
struct stat *stb,
|
||
int afp_sub,
|
||
uint8 *path, int path_len)
|
||
{
|
||
if (!unixname || !stb) return(-0x9c);
|
||
if (tru_eff_rights_exists(volume, (uint8 *)unixname, stb, TRUSTEE_M)) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=no_modify_rights path='%s' fn=0x23 sub=0x%02x result=0x8c",
|
||
afp_sub, visable_data(path, path_len), afp_sub));
|
||
return(-0x8c); /* no modify rights */
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
|
||
static int afp_set_access_time(int volume, char *unixname, struct stat *stb,
|
||
time_t new_atime)
|
||
{
|
||
struct utimbuf ut;
|
||
|
||
if (tru_eff_rights_exists(volume, (uint8 *)unixname, stb, TRUSTEE_M))
|
||
return(-0x8c);
|
||
|
||
ut.actime = new_atime;
|
||
ut.modtime = stb->st_mtime;
|
||
if (!utime(unixname, &ut))
|
||
return(0);
|
||
if (seteuid(0)) {}
|
||
if (!utime(unixname, &ut)) {
|
||
(void)reseteuid();
|
||
return(0);
|
||
}
|
||
(void)reseteuid();
|
||
return(-0x8c);
|
||
}
|
||
|
||
static int afp_set_file_information(uint8 *afp_req, int afp_len,
|
||
const char *call_name)
|
||
/*
|
||
* Conservative AFP Set File Information subset.
|
||
*
|
||
* Accept only the Novell/WebSDK fixed record layout used by real AFP clients:
|
||
* Attributes/Date/FinderInfo fields live at fixed offsets and the path is at
|
||
* offset 55 for 0x09 or 61 for 0x10 (including the AFP subfunction byte). All
|
||
* NetWare semantics still route through existing mars_nwe helpers; only genuine
|
||
* AFP metadata stays in nwatalk xattrs.
|
||
*/
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
uint16 request_mask;
|
||
int afp_sub = afp_req[0];
|
||
int is_afp20;
|
||
int path_len;
|
||
int path_off;
|
||
int path_volume;
|
||
char unixname[PATH_MAX];
|
||
char entry_path[512];
|
||
int entry_path_len = 0;
|
||
int resolved_by_entry_id = 0;
|
||
struct stat stbuff;
|
||
int result;
|
||
uint16 log_attrs = 0;
|
||
time_t log_atime = (time_t)-1;
|
||
time_t log_mtime = (time_t)-1;
|
||
int needs_afp_metadata_modify = 0;
|
||
uint8 *path_data;
|
||
uint8 *attribute_data = NULL;
|
||
uint8 *access_data = NULL;
|
||
uint8 *create_data = NULL;
|
||
uint8 *modify_data = NULL;
|
||
uint8 *backup_data = NULL;
|
||
uint8 *finder_data = NULL;
|
||
uint8 *prodos_data = NULL;
|
||
|
||
if (afp_len < 9) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, afp_sub));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
is_afp20 = (afp_req[0] == 0x10);
|
||
volume_number = afp_req[1];
|
||
request_entry_id = GET_BE32(afp_req + 2);
|
||
request_mask = GET_BE16(afp_req + 6);
|
||
|
||
/*
|
||
* WebSDK fixed layout, including afp_req[0] subfunction byte:
|
||
* 0x09: path_len @ 54, path @ 55
|
||
* 0x10: path_len @ 60, path @ 61, ProDOSInfo @ 54..59
|
||
*/
|
||
{
|
||
int websdk_path_len_off = is_afp20 ? 60 : 54;
|
||
int websdk_path_off = websdk_path_len_off + 1;
|
||
|
||
if (afp_len <= websdk_path_len_off) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_websdk_request len=%d path_len_off=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, websdk_path_len_off, afp_sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
path_len = (int)afp_req[websdk_path_len_off];
|
||
path_off = websdk_path_off;
|
||
if (path_len < 0 || afp_len < path_off + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=websdk_boundary_check len=%d path_len=%d path_off=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, path_len, path_off, afp_sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
attribute_data = afp_req + 8;
|
||
create_data = afp_req + 10;
|
||
access_data = afp_req + 12;
|
||
modify_data = afp_req + 14;
|
||
backup_data = afp_req + 18;
|
||
finder_data = afp_req + 22;
|
||
prodos_data = is_afp20 ? afp_req + 54 : NULL;
|
||
}
|
||
|
||
path_data = afp_req + path_off;
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/%d REJECT reason=no_xattr_backend fn=0x23 sub=0x%02x result=0xbf",
|
||
afp_sub, afp_sub));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
if (!path_len) {
|
||
result = afp_resolve_entry_id_path((int)volume_number, request_entry_id,
|
||
entry_path, sizeof(entry_path),
|
||
unixname, sizeof(unixname),
|
||
&path_volume, &entry_path_len);
|
||
if (result) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=entry_id_lookup vol=%u entry=%lu mask=0x%04x fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, request_mask, afp_sub,
|
||
request_entry_id, (unsigned int)(-result)));
|
||
return(result);
|
||
}
|
||
path_data = (uint8 *)entry_path;
|
||
path_len = entry_path_len;
|
||
resolved_by_entry_id = 1;
|
||
}
|
||
|
||
if (request_mask & ~(AFP_FILE_BITMAP_ATTRIBUTES | AFP_FILE_BITMAP_ACCESS_DATE |
|
||
AFP_FILE_BITMAP_CREATE_DATE | AFP_FILE_BITMAP_MODIFY_DATE |
|
||
AFP_FILE_BITMAP_BACKUP_DATE |
|
||
AFP_FILE_BITMAP_FINDER_INFO)) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=unsupported_bitmap vol=%u entry=%lu mask=0x%04x path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, request_mask,
|
||
visable_data(path_data, path_len), afp_sub, request_entry_id));
|
||
return(-0x9c); /* keep unsupported write semantics conservative */
|
||
}
|
||
if (!request_mask) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=empty_bitmap vol=%u entry=%lu path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id,
|
||
visable_data(path_data, path_len), afp_sub, request_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
|
||
if (request_mask & AFP_FILE_BITMAP_ATTRIBUTES) {
|
||
uint16 requested_attrs = GET_BE16(attribute_data);
|
||
uint16 requested_bits = requested_attrs & ~AFP_ATTR_SETCLR;
|
||
if (requested_bits & ~AFP_ATTR_NETWARE_MASK) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=unsupported_attributes attrs=0x%04x path='%s' fn=0x23 sub=0x%02x result=0x9c",
|
||
afp_sub, requested_attrs,
|
||
visable_data(path_data, path_len), afp_sub));
|
||
return(-0x9c);
|
||
}
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_CREATE_DATE)
|
||
needs_afp_metadata_modify = 1;
|
||
if (request_mask & AFP_FILE_BITMAP_BACKUP_DATE)
|
||
needs_afp_metadata_modify = 1;
|
||
if (request_mask & AFP_FILE_BITMAP_FINDER_INFO)
|
||
needs_afp_metadata_modify = 1;
|
||
if (is_afp20 && afp_info_bytes_nonzero(prodos_data, NWATALK_PRODOS_INFO_LEN))
|
||
needs_afp_metadata_modify = 1;
|
||
|
||
if (!resolved_by_entry_id) {
|
||
path_volume = afp_resolve_path_volume(path_data, path_len,
|
||
unixname, sizeof(unixname));
|
||
if (path_volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=path_resolve request_vol=%u entry=%lu mask=0x%04x path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, request_mask,
|
||
visable_data(path_data, path_len), afp_sub,
|
||
request_entry_id, (unsigned int)(-path_volume)));
|
||
return(path_volume);
|
||
}
|
||
}
|
||
|
||
if (stat(unixname, &stbuff)) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=stat request_vol=%u resolved_vol=%d entry=%lu mask=0x%04x path='%s' unix='%s' errno=%d fn=0x23 sub=0x%02x entry_hex=0x%08x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number, path_volume,
|
||
(unsigned long)request_entry_id, request_mask,
|
||
visable_data(path_data, path_len), unixname, errno,
|
||
afp_sub, request_entry_id));
|
||
return(-0x9c);
|
||
}
|
||
if (S_ISDIR(stbuff.st_mode)) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=directory_metadata request_vol=%u resolved_vol=%d path='%s' fn=0x23 sub=0x%02x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number, path_volume,
|
||
visable_data(path_data, path_len), afp_sub));
|
||
return(-0x9c);
|
||
}
|
||
|
||
if (needs_afp_metadata_modify) {
|
||
result = afp_check_metadata_modify_rights(path_volume, unixname, &stbuff,
|
||
afp_sub, path_data, path_len);
|
||
if (result < 0)
|
||
return(result);
|
||
}
|
||
|
||
if (request_mask & AFP_FILE_BITMAP_ATTRIBUTES) {
|
||
uint16 requested_attrs = GET_BE16(attribute_data);
|
||
uint16 requested_set_bits = requested_attrs & ~AFP_ATTR_SETCLR;
|
||
log_attrs = requested_attrs;
|
||
if (requested_set_bits & AFP_ATTR_NETWARE_MASK) {
|
||
result = afp_set_netware_attributes(path_volume, unixname,
|
||
&stbuff, requested_attrs);
|
||
if (result < 0)
|
||
return(result);
|
||
if (!stat(unixname, &stbuff)) {}
|
||
}
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_ACCESS_DATE) {
|
||
time_t new_atime = nw_2_un_time(access_data, access_data + 2);
|
||
if (new_atime == (time_t)-1) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=invalid_access_time path='%s' fn=0x23 sub=0x%02x result=0x8c",
|
||
afp_sub, visable_data(path_data, path_len), afp_sub));
|
||
return(-0x8c);
|
||
}
|
||
result = afp_set_access_time(path_volume, unixname, &stbuff, new_atime);
|
||
if (result < 0)
|
||
return(result);
|
||
log_atime = new_atime;
|
||
if (!stat(unixname, &stbuff)) {}
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_CREATE_DATE) {
|
||
uint16 create_date = GET_BE16(create_data);
|
||
uint16 create_time = GET_BE16(create_data + 2);
|
||
|
||
result = mars_nwe_set_file_info(unixname,
|
||
1, create_date,
|
||
1, create_time,
|
||
0, 0);
|
||
if (result < 0)
|
||
return(result);
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_MODIFY_DATE) {
|
||
time_t new_mtime = nw_2_un_time(modify_data, modify_data + 2);
|
||
if (new_mtime == (time_t)-1) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=invalid_modify_time path='%s' fn=0x23 sub=0x%02x result=0x8c",
|
||
afp_sub, visable_data(path_data, path_len), afp_sub));
|
||
return(-0x8c);
|
||
}
|
||
result = nw_utime_node(path_volume, (uint8 *)unixname, &stbuff, new_mtime);
|
||
if (result < 0)
|
||
return(result);
|
||
log_mtime = new_mtime;
|
||
if (!stat(unixname, &stbuff)) {}
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_BACKUP_DATE) {
|
||
uint16 archive_date = GET_BE16(backup_data);
|
||
uint16 archive_time = GET_BE16(backup_data + 2);
|
||
|
||
result = mars_nwe_set_archive_info(unixname,
|
||
1, archive_date,
|
||
1, archive_time,
|
||
0, 0);
|
||
if (result < 0)
|
||
return(result);
|
||
}
|
||
if (request_mask & AFP_FILE_BITMAP_FINDER_INFO) {
|
||
result = nwatalk_set_finder_info(unixname, finder_data,
|
||
NWATALK_FINDER_INFO_LEN);
|
||
if (result < 0)
|
||
return(result);
|
||
}
|
||
if (is_afp20 && afp_info_bytes_nonzero(prodos_data, NWATALK_PRODOS_INFO_LEN)) {
|
||
result = nwatalk_set_prodos_info(unixname, prodos_data,
|
||
NWATALK_PRODOS_INFO_LEN);
|
||
if (result < 0)
|
||
return(result);
|
||
}
|
||
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE vol=%d request_vol=%u entry=%lu mask=0x%04x path='%s' fields='%s%s%s%s%s%s%s' attrs=0x%04x atime=%ld mtime=%ld fn=0x23 sub=0x%02x entry_hex=0x%08x",
|
||
afp_sub, path_volume, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, request_mask,
|
||
visable_data(path_data, path_len),
|
||
(request_mask & AFP_FILE_BITMAP_ATTRIBUTES) ? "attributes " : "",
|
||
(request_mask & AFP_FILE_BITMAP_ACCESS_DATE) ? "access_time " : "",
|
||
(request_mask & AFP_FILE_BITMAP_CREATE_DATE) ? "create_time " : "",
|
||
(request_mask & AFP_FILE_BITMAP_MODIFY_DATE) ? "modify_time " : "",
|
||
(request_mask & AFP_FILE_BITMAP_BACKUP_DATE) ? "backup_time " : "",
|
||
(request_mask & AFP_FILE_BITMAP_FINDER_INFO) ? "finder_info " : "",
|
||
(is_afp20 && afp_info_bytes_nonzero(prodos_data, NWATALK_PRODOS_INFO_LEN)) ? "prodos_info" : "",
|
||
log_attrs, (long)log_atime, (long)log_mtime,
|
||
afp_sub, request_entry_id));
|
||
return(0);
|
||
}
|
||
|
||
static int afp_join_path(char *dst, int dst_len, const char *base,
|
||
const char *leaf)
|
||
{
|
||
int len;
|
||
|
||
if (!dst || dst_len < 1 || !base || !leaf) return(-1);
|
||
len = snprintf(dst, dst_len, "%s/%s", base, leaf);
|
||
if (len < 0 || len >= dst_len) return(-1);
|
||
return(0);
|
||
}
|
||
|
||
static int afp_scan_file_information(uint8 *afp_req, int afp_len,
|
||
uint8 *response, const char *call_name)
|
||
{
|
||
uint8 volume_number;
|
||
uint32 request_entry_id;
|
||
uint32 last_seen_id;
|
||
uint16 desired_count;
|
||
uint16 search_mask;
|
||
uint16 request_mask;
|
||
int afp_sub = afp_req[0];
|
||
int path_len;
|
||
int path_off;
|
||
int volume;
|
||
char unixname[PATH_MAX];
|
||
char childname[PATH_MAX];
|
||
uint8 resolved_path[255];
|
||
uint8 *scan_path_data;
|
||
int scan_path_len;
|
||
uint8 display_path[255];
|
||
int display_path_len;
|
||
DIR *dir;
|
||
struct dirent *de;
|
||
uint32 entry_id = 0;
|
||
int fallback = 0;
|
||
int seen = 0;
|
||
int result;
|
||
|
||
if (afp_len < 17) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=short_request len=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, afp_sub));
|
||
return(-0x7e); /* NCP Boundary Check Failed */
|
||
}
|
||
|
||
volume_number = afp_req[1];
|
||
request_entry_id = GET_BE32(afp_req + 2);
|
||
last_seen_id = GET_BE32(afp_req + 6);
|
||
|
||
/*
|
||
* WebSDK / nwafp.h layout only. AFP has no mars_nwe compact request
|
||
* variant here: after the subfunction byte the packet contains volume,
|
||
* base entry ID, last-seen ID, desired response count, search bitmap,
|
||
* request bitmap, path length, then the AFP path string.
|
||
*/
|
||
desired_count = GET_BE16(afp_req + 10);
|
||
search_mask = GET_BE16(afp_req + 12);
|
||
request_mask = GET_BE16(afp_req + 14);
|
||
path_len = (int)afp_req[16];
|
||
path_off = 17;
|
||
|
||
if (path_len < 0 || afp_len < path_off + path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=boundary_check len=%d path_len=%d path_off=%d fn=0x23 sub=0x%02x result=0x7e",
|
||
afp_sub, afp_len, path_len, path_off, afp_sub));
|
||
return(-0x7e);
|
||
}
|
||
|
||
if (!nwatalk_backend_available()) {
|
||
XDPRINTF((3,0, "WARN AFP 35/%d REJECT reason=no_xattr_backend fn=0x23 sub=0x%02x result=0xbf",
|
||
afp_sub, afp_sub));
|
||
return(-0xbf); /* invalid namespace */
|
||
}
|
||
|
||
scan_path_data = afp_req + path_off;
|
||
scan_path_len = path_len;
|
||
if (request_entry_id) {
|
||
result = afp_build_base_relative_path((int)volume_number,
|
||
request_entry_id,
|
||
afp_req + path_off, path_len,
|
||
resolved_path,
|
||
sizeof(resolved_path));
|
||
if (result < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=entry_id_path_build vol=%u entry=%lu last=%lu path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x last_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned long)last_seen_id,
|
||
visable_data(afp_req + path_off, path_len), afp_sub,
|
||
request_entry_id, last_seen_id, (unsigned int)(-result)));
|
||
return(result);
|
||
}
|
||
scan_path_data = resolved_path;
|
||
scan_path_len = result;
|
||
} else if (!path_len) {
|
||
XDPRINTF((2,0, "WARN AFP 35/%d REJECT reason=empty_path_without_entry_id vol=%u last=%lu search_mask=0x%04x req_mask=0x%04x fn=0x23 sub=0x%02x last_hex=0x%08x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)last_seen_id, search_mask, request_mask,
|
||
afp_sub, last_seen_id));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
volume = conn_get_kpl_unxname(unixname, sizeof(unixname), 0,
|
||
scan_path_data, scan_path_len);
|
||
if (volume < 0) {
|
||
XDPRINTF((2,0, "WARN MAP 35/%d FAIL reason=path_resolve vol=%u entry=%lu last=%lu path='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x last_hex=0x%08x result=0x%02x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned long)last_seen_id,
|
||
visable_data(scan_path_data, scan_path_len), afp_sub,
|
||
request_entry_id, last_seen_id, (unsigned int)(-volume)));
|
||
return(volume);
|
||
}
|
||
|
||
dir = opendir(unixname);
|
||
if (!dir) {
|
||
XDPRINTF((2,0, "WARN FILE 35/%d FAIL reason=opendir vol=%u entry=%lu last=%lu path='%s' unix='%s' errno=%d fn=0x23 sub=0x%02x entry_hex=0x%08x last_hex=0x%08x result=0x9c",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned long)last_seen_id,
|
||
visable_data(scan_path_data, scan_path_len), unixname, errno,
|
||
afp_sub, request_entry_id, last_seen_id));
|
||
return(-0x9c); /* Invalid Path */
|
||
}
|
||
|
||
while ((de = readdir(dir)) != NULL) {
|
||
struct stat stbuff;
|
||
int child_fallback = 0;
|
||
uint32 child_entry_id = 0;
|
||
|
||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||
continue;
|
||
if (afp_join_path(childname, sizeof(childname), unixname, de->d_name) < 0)
|
||
continue;
|
||
if (stat(childname, &stbuff))
|
||
continue;
|
||
|
||
child_entry_id = afp_get_or_create_entry_id(childname, volume, &stbuff,
|
||
&child_fallback);
|
||
|
||
if (last_seen_id && last_seen_id != 0xffffffffU && !seen) {
|
||
if (child_entry_id == last_seen_id)
|
||
seen = 1;
|
||
continue;
|
||
}
|
||
|
||
display_path_len = snprintf((char *)display_path, sizeof(display_path),
|
||
"%s%c%s", visable_data(scan_path_data, scan_path_len),
|
||
':', de->d_name);
|
||
if (display_path_len < 0 || display_path_len >= (int)sizeof(display_path)) {
|
||
display_path_len = strlen(de->d_name);
|
||
if (display_path_len > (int)sizeof(display_path))
|
||
display_path_len = sizeof(display_path);
|
||
memcpy(display_path, de->d_name, display_path_len);
|
||
}
|
||
|
||
result = afp_fill_file_info_response(childname, display_path, display_path_len,
|
||
volume, afp_req[0] == 0x11,
|
||
response + 2,
|
||
&entry_id, &fallback);
|
||
if (result < 0)
|
||
continue;
|
||
U16_TO_BE16(1, response);
|
||
closedir(dir);
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE vol=%u entry=%lu last=%lu desired=%u search_mask=0x%04x req_mask=0x%04x path='%s' count=1 reply_entry=%lu mode='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x last_hex=0x%08x reply_entry_hex=0x%08x",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned long)last_seen_id,
|
||
(unsigned)desired_count, search_mask, request_mask,
|
||
visable_data(scan_path_data, scan_path_len),
|
||
(unsigned long)(entry_id ? entry_id : child_entry_id),
|
||
(fallback || child_fallback) ? "fallback" : "",
|
||
afp_sub, request_entry_id, last_seen_id,
|
||
entry_id ? entry_id : child_entry_id));
|
||
return(2 + result);
|
||
}
|
||
|
||
closedir(dir);
|
||
XDPRINTF((3,0, "INFO AFP 35/%d DONE vol=%u entry=%lu last=%lu desired=%u path='%s' count=0 status=no_more_entries fn=0x23 sub=0x%02x entry_hex=0x%08x last_hex=0x%08x result=0xff",
|
||
afp_sub, (unsigned int)volume_number,
|
||
(unsigned long)request_entry_id, (unsigned long)last_seen_id,
|
||
(unsigned)desired_count,
|
||
visable_data(scan_path_data, scan_path_len), afp_sub,
|
||
request_entry_id, last_seen_id));
|
||
return(-0xff); /* No files found / scan complete */
|
||
}
|
||
|
||
|
||
|
||
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 .
|
||
*
|
||
* The NDK/Core Protocols synchronization list documents two earlier
|
||
* direct old compatibility calls before Log File. They are kept here
|
||
* as commented endpoint stubs until real compatibility behaviour is
|
||
* implemented or an explicit unsupported reply is chosen.
|
||
*
|
||
* NCP 0x2222/01 File Set Lock (old)
|
||
*
|
||
* Locks all shareable files that the calling station currently has
|
||
* open. The call is documented as a compatibility endpoint for
|
||
* previous NetWare versions.
|
||
*
|
||
* SDK request: no additional request fields.
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* Compared against the SDK/PDF: the request has no payload after
|
||
* FunctionCode. MARS-NWE has no active case for this endpoint, so
|
||
* this remains a TODO-only compatibility stub.
|
||
*
|
||
* TODO: not implemented in MARS-NWE yet.
|
||
*
|
||
* case 0x1 : { /_* File Set Lock (old) *_/
|
||
* completition = 0xfb;
|
||
* }
|
||
* break;
|
||
*
|
||
* NCP 0x2222/02 File Release Lock
|
||
*
|
||
* Releases the locks taken by the most recent File Set Lock request.
|
||
* This is also documented as a compatibility endpoint for previous
|
||
* NetWare versions.
|
||
*
|
||
* SDK request: no additional request fields.
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* Compared against the SDK/PDF: the request has no payload after
|
||
* FunctionCode. MARS-NWE has no active case for this endpoint, so
|
||
* this remains a TODO-only compatibility stub.
|
||
*
|
||
* TODO: not implemented in MARS-NWE yet.
|
||
*
|
||
* case 0x2 : { /_* File Release Lock *_/
|
||
* completition = 0xfb;
|
||
* }
|
||
* break;
|
||
*
|
||
* I have not found a direct 0x2222/00 synchronization call in the
|
||
* Novell/Micro Focus NCP documentation. MARS-NWE currently starts
|
||
* this switch's old file-log/file-lock handling at 0x2222/03.
|
||
*/
|
||
case 0x3 : { /* Log File */
|
||
/*
|
||
* NCP 0x2222/03 Log File (old) logs one file name in the calling
|
||
* client's file-log table. The request uses the old directory
|
||
* handle plus path layout:
|
||
*
|
||
* byte DirectoryHandle
|
||
* byte LockFlag: bit 0 asks the server to lock immediately
|
||
* word LockTimeout, in units of 1/18 second
|
||
* byte FileNameLen
|
||
* bytes FileName
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success, 0x82 no open privileges, 0x96
|
||
* server out of memory, 0xfe timeout, 0xff lock error.
|
||
*
|
||
* A logged file may name a file that does not exist yet; locking it
|
||
* reserves that name for the client. The newer Log File variants
|
||
* are 0x2222/105 and 0x2222/87/36, while 0x2222/04 and 0x2222/106
|
||
* lock the accumulated file set.
|
||
*
|
||
* Compared against the SDK/PDF: the current INPUT layout matches
|
||
* the documented old request offsets and reads LockTimeout in the
|
||
* documented Hi-Lo order. MARS-NWE records this old layout through
|
||
* nw_log_file(), which is the same table consumed by the file-set
|
||
* Lock/Release/Clear handlers below.
|
||
*/
|
||
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:
|
||
*
|
||
* 0x04 old: word LockTimeout, documented as Lo-Hi.
|
||
* 0x6a new: word LockTimeout, documented as Hi-Lo.
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success; old 0x04 documents 0xfe timeout
|
||
* and 0xff lock error, while newer 0x6a also documents
|
||
* 0x7f ERR_LOCK_WAITING.
|
||
*
|
||
* The newer 0x6a call replaces the old 0x04 call for later
|
||
* clients. MARS-NWE currently handles both calls in one branch
|
||
* and reads the timeout with GET_BE16(). That matches the 0x6a
|
||
* Hi-Lo SDK layout; the old 0x04 SDK page documents Lo-Hi. This
|
||
* comment records the wire-layout difference only; the parser is
|
||
* intentionally left unchanged in this documentation pass.
|
||
*
|
||
* 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 */
|
||
/*
|
||
* NCP 0x2222/05 Release File (old) releases one previously
|
||
* locked file but leaves it in the client's logged file table.
|
||
* NCP 0x2222/07 Clear File (old) removes one file from that
|
||
* table; if it is locked/open, the server releases/closes it.
|
||
*
|
||
* SDK request for both calls:
|
||
*
|
||
* byte DirectoryHandle
|
||
* byte FileNameLen
|
||
* bytes FileName
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion for 0x05: 0x00 success, 0x9c invalid path,
|
||
* 0xff unlock error.
|
||
* SDK completion for 0x07 additionally documents 0x96 server out
|
||
* of memory, 0x98 disk map error, 0x9b bad directory handle,
|
||
* 0xa1 directory I/O error, and 0xfd bad station number.
|
||
*
|
||
* The current INPUT layout matches the documented old request
|
||
* header offsets: FunctionCode at offset 6, DirectoryHandle at
|
||
* offset 7, FileNameLen at offset 8, and FileName at offset 9.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/06 Release File Set releases all currently logged
|
||
* or locked files, but keeps the file-log table entries.
|
||
* NCP 0x2222/08 Clear File Set releases/removes all logged files;
|
||
* open server-side file handles are invalidated by the NetWare
|
||
* semantics described in the SDK.
|
||
*
|
||
* SDK request for both calls:
|
||
*
|
||
* byte LockFlag
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* MARS-NWE currently does not read the documented LockFlag byte
|
||
* and forwards the operation directly to share_handle_lock_sets().
|
||
* This documentation patch records that parser difference only;
|
||
* endpoint behaviour is intentionally unchanged here.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/09 Log Logical Record (old) logs one synchronization
|
||
* string in the caller's logical-record table. The string can be
|
||
* up to 128 characters long.
|
||
*
|
||
* SDK request layout:
|
||
*
|
||
* byte LockFlag: bit 0 asks the server to lock immediately
|
||
* word LockTimeout, in units of 1/18 second
|
||
* byte SynchNameLen
|
||
* bytes SynchName
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success, 0x96 server out of memory,
|
||
* 0xfe timeout, 0xff lock error.
|
||
*
|
||
* The current INPUT layout matches the documented old request
|
||
* header offsets: FunctionCode at offset 6, LockFlag at offset 7,
|
||
* LockTimeout at offset 8, SynchNameLen at offset 10, and
|
||
* SynchName at offset 11.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/10 Lock Logical Record Set (old) locks all logical
|
||
* records previously logged by this client. This direct old call
|
||
* is the compatibility form of NCP 0x2222/108.
|
||
*
|
||
* SDK request layout:
|
||
*
|
||
* byte LockFlag
|
||
* word LockTimeout, in units of 1/18 second
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success, 0xfe timeout, 0xff lock error.
|
||
*
|
||
* The old SDK page documents LockTimeout as word (Lo-Hi), while
|
||
* the newer 0x6c/108 call documents word (Hi-Lo). The existing
|
||
* code reads the field with GET_BE16(). This comment records the
|
||
* parser difference only; endpoint behaviour is unchanged here.
|
||
*/
|
||
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. Both requests
|
||
* use the old direct layout:
|
||
*
|
||
* byte SynchNameLen
|
||
* bytes SynchName
|
||
*
|
||
* They return no reply data and report success or Unlock Error
|
||
* through the completion code.
|
||
*
|
||
* Compared against the SDK/PDF: the current INPUT layout matches
|
||
* the documented old request offsets: FunctionCode at offset 6,
|
||
* SynchNameLen at offset 7, and SynchName at offset 8.
|
||
*
|
||
* 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 */
|
||
/*
|
||
* NCP 0x2222/13 Release Logical Record Set releases all currently
|
||
* locked logical records but keeps the synchronization-string table.
|
||
* NCP 0x2222/14 Clear Logical Record Set releases all logical
|
||
* records and clears that table.
|
||
*
|
||
* SDK request for both calls:
|
||
*
|
||
* byte LockFlag
|
||
*
|
||
* SDK reply: no reply data.
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* MARS-NWE currently does not read the documented LockFlag byte
|
||
* and forwards the operation directly to share_handle_lock_sets().
|
||
* This documentation patch records that parser difference only;
|
||
* endpoint behaviour is intentionally unchanged here.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/18 Get Volume Info with Number returns the legacy
|
||
* NetWare 2.x/3.x volume-space summary for a numeric volume id.
|
||
*
|
||
* SDK request:
|
||
*
|
||
* byte VolumeNumber
|
||
*
|
||
* SDK reply:
|
||
*
|
||
* word SectorsPerCluster (Hi-Lo)
|
||
* word TotalVolumeClusters (Hi-Lo)
|
||
* word AvailableClusters (Hi-Lo)
|
||
* word TotalDirectorySlots (Hi-Lo)
|
||
* word AvailableDirectorySlots (Hi-Lo)
|
||
* byte VolumeName[16]
|
||
* word RemovableFlag (Hi-Lo)
|
||
*
|
||
* SDK completion: 0x00 success, 0x98 disk map error.
|
||
*
|
||
* MARS-NWE reads the one-byte VolumeNumber from requestdata[0]
|
||
* and builds the documented fixed-size reply.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/19 Get Station Number returns the calling station's
|
||
* legacy connection/station number.
|
||
*
|
||
* SDK request: no payload after FunctionCode (19).
|
||
*
|
||
* SDK reply:
|
||
*
|
||
* byte StationNumber[3]
|
||
*
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* MARS-NWE currently returns the low one-byte connection number only,
|
||
* which matches the source's long-standing old-client compatibility
|
||
* behaviour but is narrower than the three-byte SDK reply layout.
|
||
* This documentation patch records that difference only; endpoint
|
||
* behaviour is intentionally unchanged here.
|
||
*/
|
||
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 File Server Date And Time */
|
||
/*
|
||
* NCP 0x2222/20 Get File Server Date And Time returns the server
|
||
* clock in the old fixed seven-byte format.
|
||
*
|
||
* SDK request: no payload after FunctionCode (20).
|
||
*
|
||
* SDK reply:
|
||
*
|
||
* byte Year (80..99 => 1980..1999, 100..179 => 2000..2079)
|
||
* byte Month (1..12)
|
||
* byte Day (1..31)
|
||
* byte Hour (0..23)
|
||
* byte Minute (0..59)
|
||
* byte Second (0..59)
|
||
* byte DayOfWeek (0 = Sunday)
|
||
*
|
||
* SDK completion: 0x00 success.
|
||
*
|
||
* MARS-NWE builds the documented seven-byte reply from localtime().
|
||
*/
|
||
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 :
|
||
/*
|
||
* NCP 0x2222/21 is the Message NCP function group. The SDK routes
|
||
* individual message calls through a nested SubFunctionCode, for
|
||
* example the classic NetWare 1.x/2.x/3.x-compatible
|
||
* broadcast send/get and enable/disable calls.
|
||
*
|
||
* SDK group request header:
|
||
*
|
||
* word SubFuncStrucLen (Hi-Lo)
|
||
* byte SubFunctionCode
|
||
* ... subfunction-specific payload
|
||
*
|
||
* This direct dispatcher deliberately leaves the group to nwbind.
|
||
* See src/nwbind.c handle_fxx(func == 0x15) for the documented
|
||
* message subfunction layouts. The nwbind pre-parser consumes the
|
||
* three-byte group header above as:
|
||
*
|
||
* requestdata[0..1] SubFuncStrucLen
|
||
* requestdata[2] SubFunctionCode
|
||
* requestdata[3..] subfunction payload passed as rdata
|
||
*/
|
||
return(-1); /* nwbind must do this call */
|
||
|
||
case 0x16 : {
|
||
/*
|
||
* NCP 0x2222/22 is the old Directory Services function group.
|
||
* These calls use the classic nested NCP 22 group header:
|
||
*
|
||
* requestdata[0..1] SubFuncStrucLen (Hi-Lo)
|
||
* requestdata[2] SubFunctionCode
|
||
* requestdata[3..] subfunction-specific payload
|
||
*
|
||
* The first NetWare 1.x/2.x/3.x-compatible directory calls are
|
||
* documented by the SDK as:
|
||
*
|
||
* 22/00 Set Directory Handle
|
||
* 22/01 Get Directory Path
|
||
* 22/02 Scan Directory Information
|
||
* 22/03 Get Effective Directory Rights
|
||
* 22/04 Modify Maximum Rights Mask
|
||
* 22/05 Get Volume Number
|
||
* 22/06 Get Volume Name
|
||
*
|
||
* No NetWare 1.x/2.x/3.x SDK/PDF entries were found for direct 22/07,
|
||
* 22/08, or 22/09 in this group during this audit. The next
|
||
* documented direct directory calls continue at 22/0a.
|
||
*
|
||
* Layout notes below document the current parser; this patch does not
|
||
* change wire behavior.
|
||
*/
|
||
/* uint8 len = *(requestdata+1); */
|
||
uint8 *p = requestdata +2;
|
||
|
||
switch (*p) {
|
||
|
||
case 0 : {
|
||
/******** SetDirektoryHandle *************/
|
||
/*
|
||
* NCP 0x2222/22/00 Set Directory Handle.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte TargetDirectoryHandle
|
||
* byte SourceDirectoryHandle
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* The old PDF table repeats TargetDirectoryHandle for the
|
||
* second byte, but the remarks and the mars_nwe parser use
|
||
* it as SourceDirectoryHandle.
|
||
*
|
||
* Reply: completion code only; the target handle is remapped
|
||
* to the requested source/path directory.
|
||
*/
|
||
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 ***************/
|
||
/*
|
||
* NCP 0x2222/22/01 Get Directory Path.
|
||
*
|
||
* SDK request payload:
|
||
* byte TargetDirectoryHandle
|
||
*
|
||
* SDK reply payload:
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*/
|
||
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 ****************/
|
||
/*
|
||
* NCP 0x2222/22/02 Scan Directory Information.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* word StartingSearchNumber (Hi-Lo)
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte DirectoryPath[16]
|
||
* word CreationDate (Hi-Lo)
|
||
* word CreationTime (Hi-Lo)
|
||
* long OwnerTrusteeID (Hi-Lo)
|
||
* byte AccessRightsMask
|
||
* byte Reserved
|
||
* word NextSearchNumber (Hi-Lo)
|
||
*
|
||
* The current INPUT struct matches this old layout.
|
||
*/
|
||
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 ****************/
|
||
/*
|
||
* NCP 0x2222/22/03 Get Effective Directory Rights.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte EffectiveRightsMask
|
||
*/
|
||
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 ***************/
|
||
/*
|
||
* NCP 0x2222/22/05 Get Volume Number.
|
||
*
|
||
* SDK request payload:
|
||
* byte VolumeNameLen
|
||
* byte VolumeName[VolumeNameLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte VolumeNumber
|
||
*/
|
||
/* 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 ***************/
|
||
/*
|
||
* NCP 0x2222/22/06 Get Volume Name.
|
||
*
|
||
* SDK request payload:
|
||
* byte VolumeNumber
|
||
*
|
||
* SDK reply payload:
|
||
* byte VolumeNameLen
|
||
* byte VolumeName[VolumeNameLen]
|
||
*
|
||
* The SDK allows a successful empty name for an unused but
|
||
* potential volume slot.
|
||
*/
|
||
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 *********************/
|
||
/*
|
||
* NCP 0x2222/22/10 Create Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* byte DirectoryAccessMask
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: the current parser reads the documented
|
||
* handle/path fields. It does not currently consume the
|
||
* DirectoryAccessMask field beyond preserving its offset.
|
||
*/
|
||
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 *********************/
|
||
/*
|
||
* NCP 0x2222/22/11 Delete Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* byte DirectoryAccessMask
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: the current parser reads the documented
|
||
* handle/path fields. Older source comments called the
|
||
* second byte reserved, but the SDK names it
|
||
* DirectoryAccessMask; the current code ignores it.
|
||
*/
|
||
int dir_handle = (int) *(p+1);
|
||
#if 0
|
||
int reserved = (int) *(p+2); /* DirectoryAccessMask */
|
||
#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;
|
||
|
||
#if 0
|
||
case 0x0c : { /* Scan Directory for Trustees */
|
||
/*
|
||
* NCP 0x2222/22/12 Scan Directory for Trustees.
|
||
*
|
||
* Important notation note: the SDK/PDF writes this as
|
||
* "22 12" using decimal subfunction notation. The wire
|
||
* SubFunctionCode byte is therefore 12 decimal, i.e. 0x0c.
|
||
* Do not confuse it with active case 0x12 below, which is
|
||
* decimal 18 / Alloc Permanent Directory Handle.
|
||
*
|
||
* SDK/WebSDK/include context:
|
||
* NetWare servers: 2.x, 3.x, 4.x, 5.x
|
||
* APIs: NWScanDirectoryForTrustees,
|
||
* NWScanDirectoryForTrustees2,
|
||
* NWIntScanForTrustees
|
||
*
|
||
* SDK request payload after the NCP 22 group header:
|
||
* byte DirectoryHandle
|
||
* byte TrusteeSetNumber
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte DirectoryPath[16]
|
||
* word CreationDate (Hi-Lo)
|
||
* word CreationTime (Hi-Lo)
|
||
* long DirectoryOwnerID (Hi-Lo)
|
||
* long TrusteeIDSet[5] (Hi-Lo)
|
||
* byte TrusteeAccessMask[5]
|
||
*
|
||
* Status: NetWare 2.x/3.x compatibility endpoint, not
|
||
* NetWare-4-only. Leave disabled until implemented; it
|
||
* belongs in the default compatibility TODO bucket, not
|
||
* behind MARS_NWE_4.
|
||
*/
|
||
completition = 0xfb;
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
case 0xd : { /* Add Trustees to DIR */
|
||
/******** AddTrustesstoDir ***************/
|
||
/*
|
||
* NCP 0x2222/22/13 Add Trustee to Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* long TrusteeID (Hi-Lo)
|
||
* byte TrusteeAccessMask
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current INPUT layout matches the
|
||
* documented NetWare 1.x/2.x/3.x request layout.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/22/14 Delete Trustee from Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* long TrusteeID (Hi-Lo)
|
||
* byte Reserved
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current INPUT layout matches the
|
||
* documented NetWare 1.x/2.x/3.x request layout.
|
||
*/
|
||
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 *********************/
|
||
/*
|
||
* NCP 0x2222/22/15 Rename Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
* byte NewDirectoryPathLen
|
||
* byte NewDirectoryPath[NewDirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current parser matches the payload
|
||
* order. The old PDF table labels SubFuncStrucLen for
|
||
* this call as Lo-Hi even though the NCP 22 group header is
|
||
* otherwise documented and parsed as Hi-Lo; MARS-NWE does
|
||
* not use that length word after dispatch.
|
||
*/
|
||
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 **/
|
||
/*
|
||
* NCP 0x2222/22/18 Alloc Permanent Directory Handle,
|
||
* NCP 0x2222/22/19 Alloc Temporary Directory Handle, and
|
||
* NCP 0x2222/22/22 Alloc Special Temporary Directory Handle.
|
||
*
|
||
* SDK request payload for all three calls:
|
||
* byte SourceDirectoryHandle
|
||
* byte HandleName
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte NewDirectoryHandle
|
||
* byte AccessRightsMask
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* NetWare 1.x/2.x/3.x payload offsets. The three endpoint
|
||
* variants share the same request layout; only the handle
|
||
* lifetime/remap mode differs. The reply is the documented
|
||
* two-byte handle/right-mask form.
|
||
*/
|
||
struct XDATA {
|
||
uint8 dirhandle; /* new Dir Handle */
|
||
uint8 right_mask; /* effective rights 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 ****************/
|
||
/*
|
||
* NCP 0x2222/22/20 Deallocate Directory Handle.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* NetWare 1.x/2.x/3.x request layout.
|
||
*/
|
||
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 ****/
|
||
/*
|
||
* NCP 0x2222/22/21 Get Volume Info with Handle.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
*
|
||
* SDK reply payload:
|
||
* word SectorsPerCluster (Hi-Lo)
|
||
* word TotalVolumeClusters (Hi-Lo)
|
||
* word AvailableClusters (Hi-Lo)
|
||
* word TotalDirectorySlots (Hi-Lo)
|
||
* word AvailableDirectorySlots (Hi-Lo)
|
||
* byte VolumeName[16]
|
||
* word RemovableFlag (Hi-Lo)
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* request layout and emits the documented fixed-size reply
|
||
* shape. MARS-NWE derives the slot counters from Unix
|
||
* filesystem statistics rather than NetWare directory-entry
|
||
* tables.
|
||
*/
|
||
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.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
*
|
||
* SDK reply payload:
|
||
* byte ServerNetworkAddress[10]
|
||
* byte HandleID[4]
|
||
*
|
||
* Parser comparison: current parser reads the documented
|
||
* DirectoryHandle payload byte and returns the documented
|
||
* 14-byte address/handle-id reply. The local NDK PDF table
|
||
* appears to have a one-byte group-header offset typo for
|
||
* this NetWare 2.x call; the payload still aligns with the
|
||
* common NCP 22 group header used by this dispatcher.
|
||
*
|
||
* 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.
|
||
*
|
||
* SDK request payload:
|
||
* byte ServerNetworkAddress[10]
|
||
* byte HandleID[4]
|
||
*
|
||
* SDK reply payload:
|
||
* byte NewDirectoryHandle
|
||
* byte AccessRightsMask
|
||
*
|
||
* Parser comparison: current parser consumes the 14-byte
|
||
* save buffer returned by NCP 22/23 and emits the documented
|
||
* two-byte handle/right-mask reply. The local NDK PDF labels
|
||
* this as a NetWare 2.x call and appears to include an extra
|
||
* DirectoryPathLen term in SubFuncStrucLen even though the
|
||
* request table contains no path field; MARS-NWE does not
|
||
* use the length word after dispatch.
|
||
*
|
||
* 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 : {
|
||
/*
|
||
* NCP 0x2222/22/25 Set Directory Information.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* word CreationDate (Lo-Hi)
|
||
* word CreationTime (Lo-Hi)
|
||
* long OwnerID (Hi-Lo)
|
||
* byte MaximumAccessRightsMask
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current INPUT layout matches the
|
||
* documented NetWare 2.x/3.x payload offsets. The parser
|
||
* passes the date/time byte pairs through to nw_set_dir_info()
|
||
* and reads OwnerID as documented Hi-Lo.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/22/26 Get Path Name of a
|
||
* Volume-Directory Number Pair.
|
||
*
|
||
* SDK request payload:
|
||
* byte VolumeNumber
|
||
* word DirectoryEntryNumber
|
||
*
|
||
* SDK reply payload:
|
||
* byte DirectoryPathLen
|
||
* byte DirectoryPath[DirectoryPathLen]
|
||
*
|
||
* Parser comparison: current parser consumes the documented
|
||
* volume byte and 16-bit directory-entry number. The WebSDK
|
||
* table does not spell out byte order for that word; current
|
||
* code reads it as Hi-Lo and returns the documented length
|
||
* prefixed path string.
|
||
*
|
||
* 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 0x1b : { /* Scan Salvageable Files, old 22/27 */
|
||
/*
|
||
* NCP 0x2222/22/27 Scan Salvageable Files (old).
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* long Sequence (Lo-Hi)
|
||
*
|
||
* SDK reply payload: old DOS salvage directory entry,
|
||
* including next Sequence, attributes, short name, timestamps,
|
||
* deleted time/date, and deleted object ID.
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* request offsets and reads Sequence as Lo-Hi via GET_32().
|
||
* The compatibility bridge delegates reply construction to
|
||
* the shared salvage backend.
|
||
*/
|
||
int result;
|
||
if (requestlen < 8) result = -0xfb;
|
||
else result = nsp_salvage_scan_short_handle((int)p[1],
|
||
GET_32(p + 2),
|
||
responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)(-result);
|
||
}
|
||
break;
|
||
|
||
case 0x1c : { /* Recover Salvageable File, old 22/28 */
|
||
/*
|
||
* NCP 0x2222/22/28 Recover Salvageable File (old).
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* long Sequence (Hi-Lo)
|
||
* byte FileNameLen
|
||
* byte FileName[FileNameLen]
|
||
* byte NewFileNameLen
|
||
* byte NewFileName[NewFileNameLen]
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current parser validates the documented
|
||
* variable-length filename fields and reads Sequence as Hi-Lo.
|
||
* The backend call currently recovers by directory handle and
|
||
* sequence only; it does not pass the documented old/new name
|
||
* strings through. This is documented in TODO, not changed
|
||
* here.
|
||
*/
|
||
int result;
|
||
if (requestlen < 10) result = -0xfb;
|
||
else {
|
||
int file_len = (int)p[6];
|
||
if (requestlen < 10 + file_len) result = -0xfb;
|
||
else {
|
||
int new_len = (int)p[7 + file_len];
|
||
if (requestlen < 10 + file_len + new_len)
|
||
result = -0xfb;
|
||
else
|
||
result = nsp_salvage_recover_short_handle((int)p[1],
|
||
GET_BE32(p + 2),
|
||
(int)(ncprequest->task));
|
||
}
|
||
}
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)(-result);
|
||
}
|
||
break;
|
||
|
||
case 0x1d : { /* Purge Salvageable File, old 22/29 */
|
||
/*
|
||
* NCP 0x2222/22/29 Purge Salvageable File (old).
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* long Sequence (Hi-Lo)
|
||
*
|
||
* SDK reply: completion code only.
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* offsets and reads Sequence as Hi-Lo.
|
||
*/
|
||
int result;
|
||
if (requestlen < 8) result = -0xfb;
|
||
else result = nsp_salvage_purge_short_handle((int)p[1],
|
||
GET_BE32(p + 2));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)(-result);
|
||
}
|
||
break;
|
||
|
||
case 0x1e : { /* SCAN a Directory, e.g. used by ndir.exe */
|
||
/*
|
||
* NCP 0x2222/22/30 Scan a Directory.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
* byte SearchAttributes
|
||
* long Sequence (Lo-Hi)
|
||
* byte SearchPatternLen
|
||
* byte SearchPattern[SearchPatternLen]
|
||
*
|
||
* SDK reply payload: old DOS scan directory entry, beginning
|
||
* with next Sequence and followed by file/subdirectory
|
||
* metadata.
|
||
*
|
||
* Parser comparison: current INPUT layout matches the
|
||
* documented offsets, but it reads Sequence with GET_BE32()
|
||
* while the SDK documents Lo-Hi. This is recorded in TODO;
|
||
* no parser behavior is changed here.
|
||
*/
|
||
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 ???? */
|
||
/*
|
||
* NCP 0x2222/22/31 Get Directory Entry.
|
||
*
|
||
* SDK request payload:
|
||
* byte DirectoryHandle
|
||
*
|
||
* SDK reply payload: old DOS directory-entry information for
|
||
* the directory pointed to by the handle.
|
||
*
|
||
* Parser comparison: current parser consumes the documented
|
||
* handle byte. The legacy source comments treated the two
|
||
* bytes after the handle as unknown, but the SDK request has
|
||
* no such payload fields. The helper ignores those bytes and
|
||
* returns DOS directory info for the resolved handle. Keep
|
||
* this noted for old-requester verification before changing
|
||
* the accepted request length.
|
||
*/
|
||
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.
|
||
*
|
||
* SDK request payload:
|
||
* byte VolumeNumber
|
||
* long Sequence (Lo-Hi)
|
||
*
|
||
* SDK reply payload:
|
||
* byte NumberOfEntries
|
||
* repeated ObjectID / Restriction pairs, both 4K-block
|
||
* counters; the current bounded reply returns up to twelve
|
||
* entries per call.
|
||
*
|
||
* Parser comparison: current parser reads VolumeNumber from
|
||
* the first payload byte and Sequence with GET_BE32(), while
|
||
* the SDK/PDF documents Sequence as Lo-Hi. The Micro Focus
|
||
* WebSDK table shows VolumeNumber one byte later than the
|
||
* normal NCP 22 group header would imply; the local parser
|
||
* follows the common group-header alignment. Both items are
|
||
* recorded in TODO for direct-test verification.
|
||
*
|
||
* 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 : { /* Add User Disk Space Restriction */
|
||
/*
|
||
* NCP 0x2222/22/33 Add User Disk Space Restriction.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte VolumeNumber
|
||
* long ObjectID (Hi-Lo)
|
||
* long DiskSpaceLimit (Lo-Hi, 4K blocks)
|
||
*
|
||
* SDK completion: 0x00 success, 0x8c no set privileges,
|
||
* 0x96 server out of memory, 0x98 disk map error.
|
||
*
|
||
* Parser comparison: this dispatcher only recognizes the
|
||
* subfunction and forwards prehandling to nwbind. The
|
||
* forwarded parser in src/nwbind.c maps ObjectID from the
|
||
* documented payload position, but the current quota
|
||
* prehandle does not consume DiskSpaceLimit. That is
|
||
* recorded in TODO for a behavior audit; this comment does
|
||
* not change wire behavior.
|
||
*/
|
||
XDPRINTF((5, 0, "Change vol restrictions"));
|
||
}
|
||
return(-2); /* nwbind must do prehandling */
|
||
|
||
case 0x22 : { /* Remove User Disk Space Restrictions */
|
||
/*
|
||
* NCP 0x2222/22/34 Remove User Disk Space Restrictions.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte VolumeNumber
|
||
* long ObjectID (Hi-Lo)
|
||
*
|
||
* SDK completion: 0x00 success, 0x8c no set privileges,
|
||
* 0xfe user not found.
|
||
*
|
||
* Parser comparison: this dispatcher forwards the call to
|
||
* nwbind, whose quota prehandler reads ObjectID from the
|
||
* documented payload position.
|
||
*/
|
||
XDPRINTF((5, 0, "Remove vol restrictions"));
|
||
}
|
||
return(-2); /* nwbind must do prehandling */
|
||
|
||
#if 0
|
||
case 0x23 : { /* Get Directory Disk Space Restriction */
|
||
/*
|
||
* NCP 0x2222/22/35 Get Directory Disk Space Restriction.
|
||
*
|
||
* SDK/WebSDK/include context:
|
||
* NetWare servers: 3.x, 4.x, 5.x
|
||
* APIs: NWGetDirSpaceLimitList, NWGetDirSpaceInfo
|
||
*
|
||
* SDK request payload after the NCP 22 group header:
|
||
* byte DirHandle
|
||
*
|
||
* SDK reply payload:
|
||
* byte NumberOfEntries
|
||
* repeated entries:
|
||
* byte Level
|
||
* long Max (Lo-Hi)
|
||
* long Current (Lo-Hi)
|
||
*
|
||
* Status: documented NetWare 3.x/default compatibility
|
||
* endpoint. No active case exists here yet, so keep this
|
||
* disabled stub next to its numeric slot until implemented.
|
||
*/
|
||
completition = 0xfb;
|
||
}
|
||
break;
|
||
|
||
case 0x24 : { /* Set Directory Disk Space Restriction */
|
||
/*
|
||
* NCP 0x2222/22/36 Set Directory Disk Space Restriction.
|
||
*
|
||
* SDK/WebSDK/include context:
|
||
* NetWare servers: 3.x, 4.x, 5.x
|
||
* APIs: NWSetDirSpaceLimit
|
||
*
|
||
* SDK request payload after the NCP 22 group header:
|
||
* byte DirHandle
|
||
* long DiskSpaceLimit (Lo-Hi, 4K blocks)
|
||
*
|
||
* SDK completion: 0x00 success, 0x01 invalid space limit,
|
||
* 0x8c no set privileges, 0xbf invalid name space.
|
||
*
|
||
* Status: documented NetWare 3.x/default compatibility
|
||
* endpoint. No active case exists here yet, so keep this
|
||
* disabled stub next to its numeric slot until implemented.
|
||
*/
|
||
completition = 0xfb;
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
case 0x25 : { /* Set Directory Entry Information */
|
||
/*
|
||
* NCP 0x2222/22/37 Set Directory Entry Information.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
* byte SearchAttributes
|
||
* long Sequence (Lo-Hi)
|
||
* long ChangeBits (Lo-Hi)
|
||
* ... DOS file/directory or Macintosh namespace entry
|
||
*
|
||
* SDK completion: 0x00 success, 0x01 invalid parameter,
|
||
* 0x8c no set privileges, 0xbf invalid name space.
|
||
*
|
||
* Parser comparison: current struct layout matches the
|
||
* documented field order and fixed 148-byte payload, but it
|
||
* reads Sequence with GET_BE32() while the SDK documents
|
||
* Sequence as Lo-Hi. ChangeBits is handled later by
|
||
* nw_set_a_directory_entry() as Lo-Hi. This endpoint is
|
||
* used by ncopy.exe and 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 Directory for Extended Trustees */
|
||
/*
|
||
* NCP 0x2222/22/38 Scan File or Directory for Extended
|
||
* Trustees.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
* byte Sequence
|
||
* byte PathLen
|
||
* byte Path[PathLen]
|
||
*
|
||
* SDK reply payload:
|
||
* byte NumberOfEntries
|
||
* long ObjectID[20] (Hi-Lo)
|
||
* word TrusteeRights[20]
|
||
*
|
||
* Parser comparison: current parser and reply layout match
|
||
* the documented payload offsets. Rights are converted
|
||
* from mars_nwe trustee bits back to the NCP 22 mask before
|
||
* serialization.
|
||
*/
|
||
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 Extended Trustee to Directory or File */
|
||
/*
|
||
* NCP 0x2222/22/39 Add Extended Trustee to Directory or
|
||
* File.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
* long ObjectID (Hi-Lo)
|
||
* word TrusteeRights (Lo-Hi)
|
||
* byte PathLen
|
||
* byte Path[PathLen]
|
||
*
|
||
* Parser comparison: current parser matches the documented
|
||
* payload layout and converts the NCP 22 rights mask to the
|
||
* internal trustee-rights representation before calling the
|
||
* existing trustee backend.
|
||
*/
|
||
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 Directory Disk Space */
|
||
/*
|
||
* NCP 0x2222/22/40 Scan Directory Disk Space.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirectoryHandle
|
||
* byte SearchAttributes
|
||
* long Sequence (Lo-Hi)
|
||
* byte SearchPatternLen
|
||
* byte SearchPattern[SearchPatternLen]
|
||
*
|
||
* SDK reply is the extended disk-space scan entry, including
|
||
* Sequence, data/resource fork size, deleted-file fields,
|
||
* inherited rights, and namespace fork-size data.
|
||
*
|
||
* Parser comparison: current payload offsets match, but the
|
||
* Sequence is read with GET_BE32() while the SDK documents
|
||
* Lo-Hi. The current implementation delegates to
|
||
* nw_scan_a_directory(), returning the normal directory-scan
|
||
* structure rather than the full documented disk-space scan
|
||
* reply. Both differences are recorded in TODO.
|
||
*/
|
||
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;
|
||
|
||
case 0x29 : { /* Get Object Disk Usage And Restrictions */
|
||
/*
|
||
* NCP 0x2222/22/41 (SDK decimal) / wire 0x29
|
||
* 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 endpoint is forwarded to
|
||
* nwbind for bindery ObjectID-to-uid mapping; the redirected
|
||
* prehandler layout is documented at src/nwbind.c case 0x29.
|
||
* Without quota support, keep the SDK-compatible fallback
|
||
* local so builds on hosts without quota support do not need
|
||
* the quota backend.
|
||
*
|
||
* Parser comparison: request payload is VolumeNumber followed by
|
||
* ObjectID (Hi-Lo), matching the documented layout.
|
||
*/
|
||
#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 */
|
||
/*
|
||
* NCP 0x2222/22/42 Get Effective Rights for Directory Entry.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
* byte PathLen
|
||
* byte Path[PathLen]
|
||
*
|
||
* Reply:
|
||
* word AccessRights (Lo-Hi)
|
||
*
|
||
* Parser comparison: current code reads the documented
|
||
* payload offsets and returns the documented Lo-Hi rights
|
||
* word.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/22/43 Remove Extended Trustee from Dir or File.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
* long ObjectID (Hi-Lo)
|
||
* byte Unused
|
||
* byte PathLen
|
||
* byte Path[PathLen]
|
||
*
|
||
* No reply data.
|
||
*
|
||
* Parser comparison: current code reads DirHandle,
|
||
* ObjectID, the reserved byte, PathLen, and Path at the
|
||
* documented offsets.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/22/44 Get Volume and Purge Information.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte VolumeNumber
|
||
*
|
||
* Reply:
|
||
* long TotalBlocks (Hi-Lo in the PDF)
|
||
* long FreeBlocks (Lo-Hi)
|
||
* long PurgeableBlocks (Lo-Hi)
|
||
* long NotYetPurgeableBlocks (Lo-Hi)
|
||
* long TotalDirEntries (Lo-Hi)
|
||
* long AvailableDirEntries (Lo-Hi)
|
||
* byte Reserved[4]
|
||
* byte SectorsPerBlock
|
||
* byte VolumeNameLen
|
||
* byte VolumeName[VolumeNameLen]
|
||
*
|
||
* Parser comparison: request offset matches. The current
|
||
* reply maps Unix filesystem totals to the legacy shape and
|
||
* uses U32_TO_32() for all 32-bit counters; the PDF marks
|
||
* only TotalBlocks as Hi-Lo while the rest are Lo-Hi.
|
||
*/
|
||
/* 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 */
|
||
/*
|
||
* NCP 0x2222/22/45 Get Directory Information.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte DirHandle
|
||
*
|
||
* Reply:
|
||
* long TotalBlocks (Lo-Hi)
|
||
* long AvailableBlocks (Lo-Hi)
|
||
* long TotalDirEntries (Lo-Hi)
|
||
* long AvailableDirEntries (Lo-Hi)
|
||
* byte Reserved[4]
|
||
* byte SectorsPerBlock
|
||
* byte VolumeNameLen
|
||
* byte VolumeName[VolumeNameLen]
|
||
*
|
||
* Parser comparison: request offset and reply byte order
|
||
* match the documented legacy directory-information shape.
|
||
*/
|
||
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 0x2222/22/46 Rename Or Move (old).
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte SourceDirHandle
|
||
* byte SearchAttribute
|
||
* byte SourcePathComponentCount
|
||
* byte SourcePath[SourcePathLen]
|
||
* byte DestDirHandle
|
||
* byte DestPathComponentCount
|
||
* byte DestPath[DestPathLen]
|
||
*
|
||
* No reply data. This old call uses component-counted
|
||
* paths, unlike the plain length-prefixed NCP 69 rename.
|
||
*
|
||
* Parser comparison: current code decodes both component
|
||
* path lists in the documented order and supports different
|
||
* source/destination handles.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NCP 0x2222/22/47 Get Name Space Information.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte VolumeNumber
|
||
*
|
||
* Reply is a variable-length namespace/datastream inventory:
|
||
* DefinedNameSpaces, namespace names, DefinedDataStreams,
|
||
* stream-to-namespace mappings, loaded namespaces, volume
|
||
* namespaces, and volume data streams.
|
||
*
|
||
* Parser comparison: request offset matches. Reply is built
|
||
* by fill_namespace_buffer(); it reports MARS-NWE's known
|
||
* DOS/MAC/NFS/FTAM/OS2 namespace table and loaded/volume
|
||
* namespace lists in the documented variable layout.
|
||
*/
|
||
/* 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 */
|
||
/*
|
||
* NCP 0x2222/22/48 Get Name Space Directory Entry.
|
||
*
|
||
* SDK request payload after the group header:
|
||
* byte VolumeNumber
|
||
* long DOSSequence
|
||
* byte NameSpace
|
||
*
|
||
* Reply is the namespace-specific directory-entry record.
|
||
*
|
||
* Parser comparison: request offset matches. The PDF does
|
||
* not label DOSSequence byte order; current code reads it
|
||
* with GET_32() and get_namespace_dir_entry() returns the
|
||
* DOS NW_SCAN_DIR_INFO-shaped entry for the resolved base.
|
||
*/
|
||
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 */
|
||
/*
|
||
* NetWare 4.x planning bucket:
|
||
* The local NDK/Core Protocols PDF documents this as
|
||
* a NetWare 4.x/5.x call. Uploaded WebSDK/include
|
||
* material exposes the same operation through
|
||
* NWGetObjectEffectiveRights / nwbindry.h.
|
||
*
|
||
* Client32/NWCalls export:
|
||
* NWNCP22s50GetObjEffectRights
|
||
*
|
||
* Existing MARS-NWE compatibility code is intentionally
|
||
* left active. Only new, unimplemented 4.x endpoints
|
||
* should be guarded with #if MARS_NWE_4.
|
||
*
|
||
* 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
|
||
*
|
||
* NetWare 4.x planning bucket:
|
||
* The local NDK/Core Protocols PDF documents this as a
|
||
* NetWare 4.x/5.x call. Uploaded WebSDK/include material
|
||
* exposes the same operation through NWGetExtendedVolumeInfo
|
||
* in nwvol.h / nlm/nwdir.h.
|
||
*
|
||
* Existing MARS-NWE compatibility code is intentionally left
|
||
* active. Only new, unimplemented 4.x endpoints should be
|
||
* guarded with #if MARS_NWE_4.
|
||
*
|
||
* 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 */
|
||
/*
|
||
* NCP 0x2222/23 is the File Server Environment function group.
|
||
* SDK/PDF endpoint numbers are decimal; the nested wire
|
||
* SubFunctionCode is shown as hex in the switch cases below.
|
||
*
|
||
* SDK group request header:
|
||
*
|
||
* word SubFuncStrucLen (Hi-Lo)
|
||
* byte SubFunctionCode
|
||
* ... subfunction-specific payload
|
||
*
|
||
* The dispatcher consumes the three-byte group header as:
|
||
*
|
||
* requestdata[0..1] SubFuncStrucLen
|
||
* requestdata[2] SubFunctionCode
|
||
* requestdata[3..] payload passed as rdata
|
||
*
|
||
* This block only documents the file-information subfunctions
|
||
* handled locally here. Several login, bindery, queue, and
|
||
* management subfunctions in this group are intentionally forwarded
|
||
* to nwbind or to existing queue prehandlers and should be audited
|
||
* in the target file when their turn comes.
|
||
*/
|
||
/* 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 */
|
||
/*
|
||
* SDK 23/15 / wire 0x0f Scan File Information.
|
||
*
|
||
* SDK/PDF/WebSDK request payload after the group header:
|
||
* word LastSearchIndex (Lo-Hi)
|
||
* byte DirectoryHandle
|
||
* byte SearchAttributes
|
||
* byte FileNameLen
|
||
* byte FileName[FileNameLen]
|
||
*
|
||
* SDK reply:
|
||
* word NextSearchIndex (Lo-Hi)
|
||
* byte FileName[14]
|
||
* byte FileAttributes
|
||
* byte ExtendedFileAttributes
|
||
* long FileSize (Hi-Lo)
|
||
* word FileCreationDate (Hi-Lo)
|
||
* word LastAccessedDate (Hi-Lo)
|
||
* word LastUpdateDate (Hi-Lo)
|
||
* word LastUpdateTime (Hi-Lo)
|
||
* long FileOwnerID (Hi-Lo)
|
||
* word LastArchiveDate (Hi-Lo)
|
||
* word LastArchiveTime (Hi-Lo)
|
||
* byte Reserved[56]
|
||
*
|
||
* The current parser uses the documented payload offsets and
|
||
* returns the documented fixed-size information block. The SDK
|
||
* labels the search-index words as Lo-Hi, while this code reads
|
||
* and writes them with GET_BE16()/U16_TO_BE16(); keep that as
|
||
* an audit item until old requester behaviour is verified.
|
||
* Include cross-check: NWScanFileInformation() maps to the same
|
||
* SDK call through NWIntScanFileInformation().
|
||
*/
|
||
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 */
|
||
/*
|
||
* SDK 23/16 / wire 0x10 Set File Information.
|
||
*
|
||
* SDK/PDF/WebSDK request payload after the group header:
|
||
* byte FileAttributes
|
||
* byte ExtendedFileAttributes
|
||
* long FileSize (Hi-Lo; documented as ignored by the server)
|
||
* word FileCreationDate (Hi-Lo)
|
||
* word LastAccessedDate (Hi-Lo)
|
||
* word LastUpdateDate (Hi-Lo)
|
||
* word LastUpdateTime (Hi-Lo)
|
||
* long UniqueOwnerID (Hi-Lo)
|
||
* word LastArchiveDate (Hi-Lo)
|
||
* word LastArchiveTime (Hi-Lo)
|
||
* byte Reserved[56]
|
||
* byte DirectoryHandle
|
||
* byte SearchAttributes
|
||
* byte FileNameLen
|
||
* byte FileName[FileNameLen]
|
||
*
|
||
* The current parser mirrors the documented field order by
|
||
* copying the status part into NW_FILE_INFO after its 14-byte
|
||
* DOS filename prefix, then passing DirectoryHandle,
|
||
* SearchAttributes, and FileName to nw_set_file_information().
|
||
* Include cross-check: NWSetFileInformation() exposes this SDK
|
||
* call in nwfile.h.
|
||
*/
|
||
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 Create Directory
|
||
* (0x01), Create File (0x02), 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), Get DOS Name From Entry
|
||
* ID (0x12), Get Macintosh Info On Deleted Files (0x13),
|
||
* the AFP Set File Information write call (0x09), 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. The related AFP
|
||
* Get Entry ID From Name call accepts a volume/base AFP ID
|
||
* plus a modifying path string; until persistent CNID/base
|
||
* lookup exists, support the same path-backed SYS:-style
|
||
* smoke-test subset. Get Entry ID From NetWare Handle
|
||
* maps an already-open mars_nwe file handle back to its
|
||
* Unix path and returns the corresponding AFP ID. Open
|
||
* File Fork opens the same path-backed subset as a read-only
|
||
* data fork and returns a normal NetWare file handle. The
|
||
* older AFP Scan File Information (0x0a) now shares the
|
||
* same conservative directory-scan helper as the AFP 2.0
|
||
* scan call. Alloc
|
||
* Temporary Dir Handle uses the same path-backed subset and
|
||
* returns a connection-local NetWare directory handle plus
|
||
* effective rights. Then expose
|
||
* the read-only AFP Get File Information query for the same
|
||
* SYS:-style path inputs. AFP Set File Information (0x09)
|
||
* and AFP 2.0 Set File Information (0x10) accept only
|
||
* FinderInfo plus the
|
||
* metadata-only Invisible/System/Archive file attributes as
|
||
* xattr write smoke paths. AFP 2.0 Get File Information
|
||
* uses the same request/reply layout for this read-only
|
||
* path-backed subset, so route it through the same helper
|
||
* until persistent entry-id lookup and richer AFP 2.0
|
||
* metadata are implemented. These calls still require
|
||
* the optional AFP xattr metadata backend to be present; without a Mac
|
||
* namespace backend, keep returning invalid namespace.
|
||
*/
|
||
if (ufunc == 0x01 || ufunc == 0x0d) {
|
||
int result = afp_create_directory(afp_req,
|
||
afp_len, responsedata,
|
||
afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x02 || ufunc == 0x0e) {
|
||
int result = afp_create_file(afp_req,
|
||
afp_len, responsedata,
|
||
afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x03) {
|
||
int result = afp_delete_object(afp_req,
|
||
afp_len, afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x07) {
|
||
int result = afp_rename_object(afp_req,
|
||
afp_len, afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x04) {
|
||
int result = afp_get_entry_id_from_name(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x06) {
|
||
int result = afp_get_entry_id_from_netware_handle(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x08) {
|
||
int result = afp_open_file_fork(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x0b) {
|
||
int result = afp_alloc_temporary_dir_handle(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else 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 == 0x12) {
|
||
int result = afp_get_dos_name_from_entry_id(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x13) {
|
||
int result = afp_get_macintosh_info_on_deleted_file(afp_req,
|
||
afp_len, responsedata);
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x05 || ufunc == 0x0f) {
|
||
int result = afp_get_file_information(afp_req,
|
||
afp_len, responsedata,
|
||
afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x09 || ufunc == 0x10) {
|
||
int result = afp_set_file_information(afp_req,
|
||
afp_len, afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else if (ufunc == 0x0a || ufunc == 0x11) {
|
||
int result = afp_scan_file_information(afp_req,
|
||
afp_len, responsedata,
|
||
afp_call_name(ufunc));
|
||
if (result > -1) data_len = result;
|
||
else completition = (uint8)-result;
|
||
} else {
|
||
XDPRINTF((3,0,
|
||
"INFO AFP 35/%d UNKNOWN name=\"%s\" backend=\"%s\" fn=0x23 sub=0x%02x result=0xbf",
|
||
ufunc, afp_call_name(ufunc),
|
||
nwatalk_backend_available() ? "enabled" : "disabled",
|
||
ufunc));
|
||
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, requestlen,
|
||
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, "INFO BURST 97 NEGOTIATE want=%d reply=%d rw_buffer=%d echo_socket=%d flags=0x%x fn=0x61 want_hex=0x%04x reply_hex=0x%04x rw_buffer_hex=0x%04x echo_socket_hex=0x%04x",
|
||
(int) GET_BE16((uint8*)requestdata),
|
||
(int) wantsize,
|
||
(int) rw_buffer_size,
|
||
(int) sock_echo,
|
||
flags,
|
||
(int) GET_BE16((uint8*)requestdata),
|
||
(int) wantsize,
|
||
(int) rw_buffer_size,
|
||
(int) sock_echo));
|
||
} else
|
||
#endif
|
||
{
|
||
XDPRINTF((2,0, "INFO BURST 97 UNKNOWN reason=disabled fn=0x61 result=0xfb"));
|
||
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, "INFO BURST 101 CONNECT client_socket=%d max_packet=%u max_data=%u send=%u recv=%u fn=0x65 client_socket_hex=0x%04x",
|
||
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,
|
||
client_socket));
|
||
} else
|
||
#endif
|
||
{
|
||
XDPRINTF((2,0, "INFO BURST 101 UNKNOWN reason=connect_disabled fn=0x65 result=0xfb"));
|
||
nw_debug=0;
|
||
completition = 0xfb; /* unknown request */
|
||
}
|
||
break;
|
||
|
||
case 0x68 : /* NDS NCP, NDS Fragger Protokoll ?? */
|
||
XDPRINTF((2,0, "INFO NDS 104 UNKNOWN reason=fragger_unsupported fn=0x68 result=0xfb"));
|
||
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, "WARN NCP WRONGTYPE type=%d type_hex=0x%x result=0xfb", ncp_type, 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, "INFO NCP UNKNOWN type=%d func=%d ufunc=%d type_hex=0x%x fn=0x%02x sub=0x%02x result=0xfb",
|
||
ncp_type, function, subfunc, ncp_type, function, subfunc));
|
||
else
|
||
XDPRINTF((0,0, "INFO NCP UNKNOWN type=%d func=%d type_hex=0x%x fn=0x%02x result=0xfb",
|
||
ncp_type, function, ncp_type, function));
|
||
} else if (data_len){
|
||
int j = data_len;
|
||
uint8 *p = responsedata;
|
||
XDPRINTF((0,2, "DBUG NCP RESPONSE_DATA 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, "a, &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, "DBUG BURST SEND 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, "DBUG 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, "DBUG BURST RECV 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, "WARN BURST REJECT reason=recv_overflow off=%d size=%d max_recv_plus_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, "DBUG 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, "DBUG BURST READ_ALIGNMENT off=%d size=%d", foffset, fsize));
|
||
}
|
||
if (size > -1) {
|
||
U32_TO_32(0, xdata->resultcode);
|
||
U32_TO_BE32(size, xdata->readbytes);
|
||
XDPRINTF((2, 0, "DBUG 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, "DBUG 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, "DBUG 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, "WARN BURST UNKNOWN function=0x%x result=ignored", 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, "DBUG BURST MISSING_REQUEST seq=%d missing=%d",
|
||
burstsequence, missing));
|
||
while (missing--){
|
||
int offs=GET_BE32(p);
|
||
int size=GET_BE16(p+4);
|
||
XDPRINTF((3, 0, "DBUG BURST RESEND off=%d size=%d", offs, size));
|
||
handle_burst_response(offs, size);
|
||
p+=6;
|
||
}
|
||
}
|
||
} else {
|
||
XDPRINTF((1, 0, "WARN BURST REJECT reason=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, "DBUG NCP DIRECT_RESPONSE type=0x3333 completion=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, "DBUG BURST PACKET 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);
|
||
}
|