Files
mars-nwe/src/nwconn.c
2026-06-02 00:18:10 +02:00

7194 lines
296 KiB
C
Raw Blame History

/* nwconn.c 18-Apr-00 */
/* one process / connection */
/* (C)opyright (C) 1993,2000 Martin Stover, Marburg, Germany
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* history since 21-Apr-00
*
* mst:25-Apr-00: added routines for sending nwconn data to nwbind
*
*/
#include "net.h"
#if 1
# define LOC_RW_BUFFERSIZE RW_BUFFERSIZE
#else
# define LOC_RW_BUFFERSIZE 512
#endif
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#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, &quota, &used);
} else {
result = -0x85;
}
data_len = 8;
if (result == 0) {
U32_TO_32(quota, xdata->restriction);
U32_TO_32(used, xdata->inuse);
} else {
U32_TO_32(0x40000000, xdata->restriction);
U32_TO_32(0x0, xdata->inuse);
completition = (uint8) -result;
}
}
break;
default : completition = 0xfb;
}
break;
}
case 0x17 : { /* FILE SERVER ENVIRONMENT */
uint8 ufunc = *(requestdata+2);
switch (ufunc) {
case 0x14: /* Login Objekt, unencrypted passwords */
case 0x18: { /* crypt_keyed LOGIN */
int grpcount = * (int*)(bindresponse + 4 * sizeof(int));
uint32 *grps = (uint32*)(bindresponse + 5 * sizeof(int));
int unxloginlen = (int)*(uint8*)(grps+grpcount);
uint8 *unxloginname = (uint8*)(grps+grpcount)+1;
uint8 objname[48];
/* ncpserv have changed the structure */
if (ufunc==0x14) {
xstrmaxcpy(objname, requestdata+6, (int) *(requestdata+5));
} else if (ufunc==0x18){
xstrmaxcpy(objname, requestdata+14, (int) *(requestdata+13));
} else objname[0]='\0';
set_nw_user(*((int*)bindresponse), /* gid */
*((int*)(bindresponse+sizeof(int))), /* uid */
*((int*)(bindresponse + 2 * sizeof(int))), /* id_flags */
*((uint32*)(bindresponse + 3 * sizeof(int))), /* id */
objname, /* login name */
unxloginlen, unxloginname,
grpcount, grps);
}
break;
case 0x68: /* create queue job and file old */
case 0x79: { /* create queue job and file */
/* nwbind made prehandling */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x79 or 0x68 */
uint8 queue_id[4]; /* Queue ID */
uint8 queue_job[280]; /* oldsize is 256 */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
uint8 *qjob = bindresponse;
int result = creat_queue_job( (int) ncprequest->task,
q_id, qjob,
responsedata,
(ufunc == 0x68) );
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
}
break;
case 0x69: /* close file and start queue old ?? */
case 0x7f: { /* close file and start queue */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x7f or 0x69 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_id[4]; /* result from creat queue */
/* if 0x69 then only 2 byte ! */
} *input = (struct INPUT *) (ncprequest);
struct RINPUT {
uint8 client_area[152];
uint8 prc_len; /* len of printcommand */
uint8 prc[1]; /* printcommand */
} *rinput = (struct RINPUT *) (bindresponse);
uint32 q_id = GET_BE32(input->queue_id);
int job_id = (ufunc==0x69) ? GET_BE16(input->job_id)
: GET_BE16(input->job_id);
int result = close_queue_job2(q_id, job_id,
rinput->client_area,
rinput->prc,
rinput->prc_len);
if (result < 0) completition = (uint8)-result;
}
break;
case 0x71 : /* service queue job (old) */
case 0x7c : { /* service queue job */
struct INPUT {
uint8 header[7]; /* Requestheader */
uint8 packetlen[2]; /* low high */
uint8 func; /* 0x7c,0x71 */
uint8 queue_id[4]; /* Queue ID */
uint8 job_typ[2]; /* service typ */
} *input = (struct INPUT *) (ncprequest);
uint32 q_id = GET_BE32(input->queue_id);
uint8 *qjob = bindresponse;
int result = service_queue_job((int)ncprequest->task,
q_id, qjob,
responsedata,
ufunc==0x71);
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
}
break;
default : completition = 0xfb;
}
}
break;
default : completition = 0xfb;
} /* switch */
ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len);
}
#if ENABLE_BURSTMODE
static int send_burst(int offset, int datasize, int flags)
{
BURSTPACKET *sb=burst_w->sendburst;
U32_TO_BE32(burst_w->packet_sequence++, sb->packet_sequence);
U32_TO_BE32(offset, sb->burstoffset);
U16_TO_BE16(datasize, sb->datasize);
U16_TO_BE16(0, sb->missing);
sb->flags = (uint8)flags;
memcpy(sb+1, burst_w->send_buf+offset, datasize);
burst_w->ud.udata.len =
burst_w->ud.udata.maxlen = datasize+sizeof(BURSTPACKET);
XDPRINTF((8, 0, "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);
}