/* 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 #include #include #include #include #if !CALL_NWCONN_OVER_SOCKET #include #include #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, "NCP REQUEST: func=0x%02x, ufunc=0x%02x, seq:%03d, task:%02d", (int)ncprequest->function, ufunc, (int)ncprequest->sequence, (int)ncprequest->task)); } else { XDPRINTF((1, 0, "Got NCP:type:0x%x, seq:%d, task:%d, func=0x%x", ncp_type, (int)ncprequest->sequence, (int)ncprequest->task, (int)ncprequest->function)); } if (requestlen > 0){ int j = requestlen; uint8 *p=requestdata; XDPRINTF((0, 2, "len %d, DATA:", j)); while (j--) { int c = *p++; if (c > 32 && c < 127) XDPRINTF((0, 3,",\'%c\'", (char) c)); else XDPRINTF((0,3, ",0x%x", c)); } XDPRINTF((0,1,NULL)); } } #if TEST_FNAME static int test_handle = -1; #endif static void handle_nwbind_request(void) /* mst:25-Apr-00 */ { int result = 0; char buf[IPX_MAX_DATA]; char *data = &(buf[sizeof(NCPRESPONSE)]); int data_len = 0; switch (ncprequest->function) { case 0x1: { /* get number and handles of open files */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 offset[4]; /* handle offset */ } *input = (struct INPUT *) (ncprequest); struct XDATA { uint8 count_handles[4]; uint8 handles[4]; } *xdata = (struct XDATA*) data; int handles = nw_get_count_open_files(xdata->handles, GET_BE32(input->offset)); U32_TO_BE32(handles, xdata->count_handles); data_len = (handles+1) * 4; } break; default : result = 0xfb; } { NCPRESPONSE *resp = (NCPRESPONSE*)&buf; ipxAddr_t to_addr; memcpy(&to_addr, &my_addr, sizeof(ipxAddr_t)); U16_TO_BE16(sock_nwbind, to_addr.sock); ud.addr.buf = (char*)&to_addr; resp->type[0] = resp->type[1]=0x32; resp->sequence = ncprequest->sequence; resp->connection = ncprequest->connection; resp->task = ncprequest->task; resp->high_connection = ncprequest->high_connection; resp->completition = result; resp->connect_status = 0; ud.udata.len = ud.udata.maxlen = sizeof(NCPRESPONSE) + data_len; ud.udata.buf = (char*)resp; XDPRINTF((3, 0, "send reply to nwbind")); result = t_sndudata(FD_NCP_OUT, &ud); ud.addr.buf = (char*)&from_addr; ud.udata.buf = (char*)&ipxdata; } } static const char *afp_call_name(int ufunc) { switch (ufunc) { case 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=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; } 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=0x%04x path='%s' reply_entry=%lu mode='%s' fn=0x23 sub=0x%02x entry_hex=0x%08x reply_entry_hex=0x%08x", afp_sub, (unsigned int)volume_number, (unsigned long)request_entry_id, request_mask, visable_data(path_data, path_data_len), (unsigned long)entry_id, fallback ? "fallback" : "", afp_sub, request_entry_id, entry_id)); 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 . */ case 0x3 : { /* Log File */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 dir_handle; uint8 lock_flag; /* 0 = log, 1 = lock excl */ /* 3 = lock shared */ uint8 timeout[2]; /* HI LO */ uint8 len; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); int result = nw_log_file( input->lock_flag, GET_BE16(input->timeout), input->dir_handle, input->len, input->path); if (result) completition = (uint8) -result; } break; case 0x4 : /* Lock File Set (old) */ case 0x6a : { /* Lock File Set */ /* * NCP 0x2222/04 Lock File Set (old) and NCP 0x2222/106 Lock * File Set lock all files logged by the calling client's current * task. * * SDK request: 2-byte Lock Timeout, in units of 1/18 second; * 0 means No Wait. * SDK reply: no reply data. * SDK completion: 0x00 success, 0xfe timeout, 0xff lock error. * * The newer 0x6a call replaces the old 0x04 call but has the same * request layout for the file set lock itself. MARS-NWE records * file-set members through Log File (0x03), then uses the shared * set handler for Lock/Release/Clear File Set operations. */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 timeout[2]; /* HI LO */ } *input = (struct INPUT *) (ncprequest); int result = share_handle_lock_sets( 1, /* File Set */ 0, /* Lock logged entries */ GET_BE16(input->timeout)); if (result) completition = (uint8) -result; } break; case 0x5 : /* Release File */ case 0x7 : { /* Clear File, removes file from logset */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 dir_handle; uint8 len; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); int result = nw_log_file( (function==0x7) ? -2 /* unlock and unlog */ : -1, /* unlock */ 0, input->dir_handle, input->len, input->path); if (result) completition = (uint8) -result; } break; case 0x6 : /* Release File Set */ case 0x8 : { /* Clear File Set */ int result = share_handle_lock_sets( 1, /* File Set */ (function==0x8) ? -2 /* Clear */ : -1, /* Release */ 0); if (result) completition = (uint8) -result; } break; case 0x9 : { /* Log Logical Record */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 lock_flag; /* 0 = log */ /* 1 = exclusive lock */ /* 3 = shared ro lock */ uint8 timeout[2]; /* HI LO */ uint8 len; /* synch name len */ uint8 name[2]; /* synch name */ } *input = (struct INPUT *) (ncprequest); int result = nw_log_logical_record( (int) input->lock_flag, (int) GET_BE16(input->timeout), (int) input->len, input->name); if (result) completition = (uint8) -result; } break; case 0xa : { /* Log Logical Record Set */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 lock_flag; /* 0 = log */ /* 1 = exclusive lock */ /* 3 = shared ro lock */ uint8 timeout[2]; /* HI LO */ } *input = (struct INPUT *) (ncprequest); int result = share_handle_lock_sets( 2, /* Logical Record Set */ (int) input->lock_flag, (int) GET_BE16(input->timeout) ); if (result) completition = (uint8) -result; } break; case 0xb : /* Clear Logical Record */ case 0xc : { /* Release Logical Record */ /* * SDK: NCP 0x2222/11 Clear Logical Record removes one logical * record from the calling task's synchronization table. * * SDK: NCP 0x2222/12 Release Logical Record releases/unlocks one * synchronization string held by the calling client. The request * is a one-byte Synch Name Length followed by the Synch Name * (up to 128 bytes), returns no reply data, and reports success or * Unlock Error through the completion code. * * Important semantic difference: Release Logical Record leaves the * string in the caller's synchronization string table so a later * Lock Logical Record Set can relock it. Clear Logical Record * unlocks and removes it from the table. */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 len; /* synch name len */ uint8 name[2]; /* synch name */ } *input = (struct INPUT *) (ncprequest); int result = nw_log_logical_record( (function == 0xb) ? -2 /* unlock + unlog */ : -1, /* unlock */ 0, (int) input->len, input->name); if (result) completition = (uint8) -result; } break; case 0xe : /* Clear Logical Record Set */ case 0xd : { /* Release Logical Record Set */ int result = share_handle_lock_sets( 2, /* Logical Record Set */ (function==0xe) ? -2 /* Clear */ : -1, /* Release */ 0); if (result) completition = (uint8) -result; } break; case 0x12 : { /* Get Volume Info with Number */ int volume = (int)*requestdata; struct XDATA { uint8 sec_per_block[2]; uint8 total_blocks[2]; uint8 avail_blocks[2]; uint8 total_dirs[2]; uint8 avail_dirs[2]; uint8 name[16]; uint8 removable[2]; } *xdata = (struct XDATA*) responsedata; int result; memset(xdata, 0, sizeof(struct XDATA)); if ((result = nw_get_volume_name(volume, xdata->name, sizeof(xdata->name)))>-1){ struct fs_usage fsp; if (!nw_get_fs_usage(xdata->name, &fsp, entry8_flags&0x40 )) { int sector_scale=1; while (fsp.fsu_blocks/sector_scale > 0xffff) sector_scale+=2; U16_TO_BE16(sector_scale, xdata->sec_per_block); U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks); U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks); U16_TO_BE16(fsp.fsu_files, xdata->total_dirs); U16_TO_BE16(fsp.fsu_ffree, xdata->avail_dirs); if ( get_volume_options(volume) & VOL_OPTION_REMOUNT) { U16_TO_BE16(1, xdata->removable); } else { U16_TO_BE16(0, xdata->removable); } } data_len = sizeof(struct XDATA); } else completition = (uint8) -result; } break; case 0x13 : { /* Get Station Number */ /* * This is the old one-byte connection number call used by * DOS requesters before the richer NCP 23 connection services. * The SDK describes the client-visible API as * NWGetConnectionNumber(); older low-level documentation names the * underlying simple NCP 0x13 "Get Station Number". */ if (act_connection > 0 && act_connection < 256) { *responsedata=(uint8) act_connection; data_len = 1; } else { XDPRINTF((1, 0, "Get Station Number failed, conn=%d", act_connection)); completition = 0xff; } } break; case 0x14 : { /* GET DATE und TIME */ struct SERVER_DATE { uint8 year; uint8 mon; uint8 day; uint8 std; uint8 min; uint8 sec; uint8 day_of_week; } *mydate = (struct SERVER_DATE*) responsedata; struct tm *s_tm; time_t timer; time(&timer); s_tm = localtime(&timer); mydate->year = (uint8) s_tm->tm_year; mydate->mon = (uint8) s_tm->tm_mon+1; mydate->day = (uint8) s_tm->tm_mday; mydate->std = (uint8) s_tm->tm_hour; mydate->min = (uint8) s_tm->tm_min; mydate->sec = (uint8) s_tm->tm_sec; mydate->day_of_week = (uint8) s_tm->tm_wday; /* Wochentag */ data_len = sizeof(struct SERVER_DATE); } break; case 0x15 : return(-1); /* nwbind must do this call */ case 0x16 : { /* uint8 len = *(requestdata+1); */ uint8 *p = requestdata +2; switch (*p) { case 0 : { /******** SetDirektoryHandle *************/ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 target_dir_handle; /* to change */ uint8 source_dir_handle; uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); completition = (uint8)-nw_set_dir_handle((int)input->target_dir_handle, (int)input->source_dir_handle, input->path, (int)input->pathlen, (int)(ncprequest->task)); } break; case 0x1 : { /******** GetDirektoryPATH ***************/ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; } *input = (struct INPUT *) (ncprequest); struct XDATA { uint8 len; uint8 name[256]; } *xdata = (struct XDATA*) responsedata; int result = nw_get_directory_path((int)input->dir_handle, xdata->name, sizeof(xdata->name)); if (result > -1){ xdata->len = (uint8) result; data_len = result + 1; xdata->name[result] = '\0'; XDPRINTF((5,0, "GetDirektoryPATH=%s", xdata->name)); } else completition = (uint8)-result; } break; case 0x2 : { /* Scan Direktory Information */ /******** Scan Dir Info ****************/ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Verzeichnis Handle */ uint8 sub_dir_nmbr[2]; /* HI LOW */ /* firsttime 1 */ uint8 len; /* kann auch 0 sein */ uint8 path[2]; } *input = (struct INPUT *) (ncprequest); struct XDATA { uint8 sub_dir_name[16]; uint8 create_date_time[4]; uint8 owner_id[4]; /* HI LOW */ uint8 max_right_mask; /* inherited right mask */ uint8 reserved; /* Reserved by Novell */ uint8 sub_dir_nmbr[2]; /* HI LOW */ } *xdata = (struct XDATA*) responsedata; int result; memcpy(xdata->sub_dir_nmbr, input->sub_dir_nmbr, 2); result = nw_scan_dir_info((int)input->dir_handle, input->path, (int)input->len, xdata->sub_dir_nmbr, xdata->sub_dir_name, xdata->create_date_time, xdata->owner_id); if (result > -1){ xdata->max_right_mask = (uint8)result; data_len = sizeof(struct XDATA); XDPRINTF((5,0,"Scan Dir Info max_right_mask=%d", (int)result)); } else completition = (uint8)-result; } break; case 0x3 : { /* Get Direktory Rights */ /******** Get Eff Dir Rights ****************/ struct XDATA { uint8 eff_right_mask; /* Effektive Right to Dir, old! */ } *xdata = (struct XDATA*) responsedata; int result = nw_get_eff_dir_rights( (int)*(p+1), p+3, (int)*(p+2), 0); if (result > -1) { int ncp22_rights = trustee_v3_to_ncp22_rights(result); xdata->eff_right_mask = (uint8)ncp22_rights; data_len = 1; XDPRINTF((5,0,"NCP22/3 Get Dir Rights=%d ncp22=%d", (int)result, (int)ncp22_rights)); } else completition = (uint8) -result; } break; case 0x4 : { /* Modify Max Right Mask */ /******** MODIFY MAX RIGHT MASK ****************/ /* * Request (NetWare 2.x compatible NCP 22/4): * byte directory handle * byte grant rights mask * byte revoke rights mask * byte path length * path bytes * * New mask is: (old_mask | grant_mask) & ~revoke_mask. * * No reply data. */ int result = nw_modify_max_right_mask( (int)*(p+1), p+5, (int)*(p+4), (int)*(p+2), (int)*(p+3)); if (result) completition = (uint8)-result; } break; case 0x5 : { /* Get Volume Number 0 .. 31 */ /******** GetVolume Number ***************/ /* p+1 = namelen */ /* p+2 = data z.b 'SYS' */ struct XDATA { uint8 volume; /* Nummer */ } *xdata = (struct XDATA*) responsedata; int result = nw_get_volume_number(p+2, (int)*(p+1)); if (result > -1) { xdata->volume = (uint8) result; data_len = 1; } else completition = (uint8) -result; } break; case 0x6 : { /* Get Volume Name from 0 .. 31 */ /******** Get Volume Name ***************/ struct XDATA { uint8 namelen; uint8 name[16]; } *xdata = (struct XDATA*) responsedata; int result = nw_get_volume_name((int)*(p+1), xdata->name, sizeof(xdata->name)); if (result > -1) { xdata->namelen = (uint8) result; data_len = result+1; } else completition = (uint8) -result; } break; case 0xa : { /* create directory */ /******** Create Dir *********************/ int dir_handle = (int) *(p+1); #if 0 int rightmask = (int) *(p+2); #endif int pathlen = (int) *(p+3); uint8 *path = p+4; int code = nw_mk_rd_dir(dir_handle, path, pathlen, 1); if (code) completition = (uint8) -code; } break; case 0xb : { /* delete dirrctory */ /******** Delete DIR *********************/ int dir_handle = (int) *(p+1); #if 0 int reserved = (int) *(p+2); /* Res. by NOVELL */ #endif int pathlen = (int) *(p+3); uint8 *path = p+4; int code = nw_mk_rd_dir(dir_handle, path, pathlen, 0); if (code) completition = (uint8) -code; } break; case 0xd : { /* Add Trustees to DIR */ /******** AddTrustesstoDir ***************/ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Verzeichnis Handle */ uint8 trustee_id[4]; /* Trustee Object ID */ uint8 trustee_right_mask; uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); uint32 trustee_id = GET_BE32(input->trustee_id); int ncp22_rights = (int)input->trustee_right_mask; int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights); int copylen = input->pathlen; uint8 pathbuf[256]; int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff); int result; ncp23_debug_dump("NCP22/0D AddTrustee raw", (uint8 *)&input->dir_handle, raw_len); if (copylen > 255) copylen = 255; memcpy(pathbuf, input->path, copylen); pathbuf[copylen] = '\0'; result = nw_add_trustee( input->dir_handle, input->path, input->pathlen, trustee_id, trustee_rights, 0); XDPRINTF((5,0, "NCP22/0D AddTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%02x rights=0x%03x rc=%d", input->dir_handle, input->pathlen, pathbuf, (unsigned long)trustee_id, ncp22_rights, trustee_rights, result)); if (result) completition = (uint8) -result; } break; case 0xe : { /* remove trustees */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Handle */ uint8 trustee_id[4]; /* Trustee Object ID */ uint8 reserved; uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); uint32 trustee_id = GET_BE32(input->trustee_id); int copylen = input->pathlen; uint8 pathbuf[256]; int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff); int result; ncp23_debug_dump("NCP22/0E DelTrustee raw", (uint8 *)&input->dir_handle, raw_len); if (copylen > 255) copylen = 255; memcpy(pathbuf, input->path, copylen); pathbuf[copylen] = '\0'; result = nw_del_trustee( input->dir_handle, input->path, input->pathlen, trustee_id, 0); /* normal */ XDPRINTF((5,0, "NCP22/0E DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d", input->dir_handle, input->pathlen, pathbuf, (unsigned long)trustee_id, result)); if (result) completition = (uint8) -result; } break; case 0xf : { /* rename dir */ /******** Rename DIR *********************/ int dir_handle = (int) *(p+1); int oldpathlen = (int) *(p+2); uint8 *oldpath = p+3; int newpathlen = (int) *(oldpath + oldpathlen); uint8 *newpath = oldpath + oldpathlen + 1; int code = mv_dir(dir_handle, oldpath, oldpathlen, newpath, newpathlen); if (code) completition = (uint8) -code; } break; case 0x12 : /* Allocate Permanent Dir Handle */ /******** Allocate Permanent DIR Handle **/ case 0x13 : /* Allocate Temp Dir Handle */ /******** Allocate Temp DIR Handle **/ case 0x16 : { /* Allocate Special Temp Dir Handle */ /******** Allocate spez temp DIR Handle **/ struct XDATA { uint8 dirhandle; /* new Dir Handle */ uint8 right_mask; /* 0xff effektive Right MAsk ? */ } *xdata = (struct XDATA*) responsedata; int eff_rights; int dirhandle = nw_alloc_dir_handle( (int) *(p+1), p+4, (int)*(p+3), (int)*(p+2), (*p==0x12) ? 0 : ((*p==0x13) ? 1 : 2), (int)(ncprequest->task), &eff_rights); if (dirhandle > -1){ xdata->dirhandle = (uint8) dirhandle; xdata->right_mask = eff_rights; data_len = sizeof(struct XDATA); } else completition = (uint8) -dirhandle; } break; case 0x14 : { /* deallocate Dir Handle */ /******** Free DIR Handle ****************/ int err_code = nw_free_dir_handle((int)*(p+1), (int)(ncprequest->task)); if (err_code) completition = (uint8) -err_code; } break; case 0x15 : { /* liefert Volume Information */ /******** Get Volume Info with Handle ****/ struct XDATA { uint8 sectors[2]; uint8 total_blocks[2]; uint8 avail_blocks[2]; uint8 total_dirs[2]; /* anz dirs */ uint8 avail_dirs[2]; /* free dirs */ uint8 name[16]; /* SYS Name */ uint8 removable[2]; } *xdata = (struct XDATA*)responsedata; int result = nw_get_vol_number((int)*(p+1)); memset(xdata, 0, sizeof(struct XDATA)); if (result > -1) { int volume = result; result = nw_get_volume_name(volume, xdata->name, sizeof(xdata->name) ); if (result > -1) { struct fs_usage fsp; if (!nw_get_fs_usage(xdata->name, &fsp, entry8_flags&0x40 )) { int sector_scale=1; while (fsp.fsu_blocks/sector_scale > 0xffff) sector_scale+=2; U16_TO_BE16(sector_scale, xdata->sectors); U16_TO_BE16(fsp.fsu_blocks/sector_scale, xdata->total_blocks); U16_TO_BE16(fsp.fsu_bavail/sector_scale, xdata->avail_blocks); U16_TO_BE16(fsp.fsu_files, xdata->total_dirs); U16_TO_BE16(fsp.fsu_ffree, xdata->avail_dirs); if (get_volume_options(volume) & VOL_OPTION_REMOUNT) { U16_TO_BE16(1, xdata->removable); } else { U16_TO_BE16(0, xdata->removable); } } data_len = sizeof(struct XDATA); XDPRINTF((5,0,"GIVE VOLUME INFO from :%s:", xdata->name)); result = 0; } } completition = (uint8)-result; } break; case 0x17 : { /* Extract a Base Handle */ /* * NCP 0x2222/22/23 Extract a Base Handle. * * WebSDK: request carries DirectoryHandle. Reply returns a * 10-byte ServerNetworkAddress plus a 4-byte opaque * HandleID. The handle must have been allocated with Alloc * Permanent Directory Handle. The returned HandleID is used * by NCP 0x16/0x18 Restore an Extracted Base Handle. * * MARS-NWE keeps the HandleID opaque to the client and maps * it to the current connection's saved volume/path tuple. */ struct XDATA { uint8 server_network_address[10]; uint8 handle_id[4]; } *xdata = (struct XDATA*)responsedata; int result; memcpy(xdata->server_network_address, my_addr.net, 4); memcpy(xdata->server_network_address+4, my_addr.node, 6); result = nw_extract_base_handle((int)*(p+1), xdata->handle_id); if (result) completition = (uint8)-result; else data_len = sizeof(struct XDATA); } break; case 0x18 : { /* Restore Directory Handle */ /* * NCP 0x2222/22/24 Restore an Extracted Base Handle. * * WebSDK: request carries the 10-byte ServerNetworkAddress * and 4-byte HandleID returned by Extract a Base Handle. * Reply returns NewDirectoryHandle and AccessRightsMask. * * The old SDK API exposes this as NWRestoreDirectoryHandle() * and uses a 14-byte save buffer produced by * NWSaveDirectoryHandle(). Rust nwserver and lwared do not * implement this older save/restore pair; newer clients * typically use the ordinary allocate/set directory-handle * calls instead. Keep this implementation conservative and * connection-local rather than guessing a global NetWare * directory-base number. */ struct INPUT { uint8 server_network_address[10]; uint8 handle_id[4]; } *input = (struct INPUT*)(p+1); struct XDATA { uint8 dirhandle; uint8 right_mask; } *xdata = (struct XDATA*)responsedata; int eff_rights = 0; int result; if (memcmp(input->server_network_address, my_addr.net, 4) || memcmp(input->server_network_address+4, my_addr.node, 6)) { completition = 0xfd; /* Bad Station Number */ break; } result = nw_restore_base_handle(input->handle_id, (int)(ncprequest->task), &eff_rights); if (result > -1) { xdata->dirhandle = (uint8)result; xdata->right_mask = (uint8)eff_rights; data_len = sizeof(struct XDATA); } else completition = (uint8)-result; } break; case 0x19 : { /* Set Directory Information * Modifies basic directory information as creation date and * directory rights mask. DOS namespace. */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; uint8 creation_date[2]; uint8 creation_time[2]; uint8 owner_id[4]; uint8 new_max_rights; uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); int result = nw_set_dir_info( input->dir_handle, input->path, input->pathlen, GET_BE32(input->owner_id), (int)input->new_max_rights, input->creation_date, input->creation_time); if (result<0) completition = (uint8) -result; /* No REPLY */ } break; case 0x1a : { /* Get Pathname of A Volume Dir Pair */ /* * Old NetWare 2.x compatibility call: * byte volume * word directory entry number (Hi-Lo) * * Reply: * byte path length * byte path[path length] * * This is the older string form of the NCP23/F3 mapping. * NCP23/F3 is preferred for 32-bit directory numbers and * namespace-aware callers. */ struct XDATA { uint8 pathlen; uint8 pathname[255]; } *xdata = (struct XDATA*)responsedata; int volume = (int)*(p+1); uint32 dirnum = (uint32)GET_BE16(p+2); uint8 comps[256]; uint8 pathbuf[255]; int result; int pos = 0; int out = 0; int first = 1; result = map_directory_number_to_path(volume, dirnum, 0, comps, sizeof(comps)); if (result > -1) { while (pos < result) { int l = (int)comps[pos++]; if (l <= 0 || pos + l > result) { result = -0x9c; break; } if (!first) { if (out >= (int)sizeof(pathbuf)) { result = -0x9c; break; } pathbuf[out++] = '\\'; } if (out + l > (int)sizeof(pathbuf)) { result = -0x9c; break; } memcpy(pathbuf + out, comps + pos, l); out += l; pos += l; first = 0; } } if (result > -1) { xdata->pathlen = (uint8)out; memcpy(xdata->pathname, pathbuf, out); data_len = out + 1; } else completition = (uint8)(-result); } break; case 0x1b : { /* Scan Salvageable Files, old 22/27 */ 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 */ 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 */ 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 */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Verzeichnis Handle */ uint8 attrib; /* Search Attrib z.B. 0x6 */ uint8 searchsequence[4]; /* 32 bit */ uint8 len; uint8 data[2]; } *input = (struct INPUT *) (ncprequest); int result = nw_scan_a_directory( responsedata, input->dir_handle, input->data, input->len, input->attrib, GET_BE32(input->searchsequence)); if (result > -1) data_len = result; else completition = (uint8) (-result); } break; case 0x1f : { /* SCAN a root dir ???? */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Verzeichnis Handle */ uint8 dont_know1; /* ???? 0xc0 */ uint8 dont_know2; /* ???? 0xfa */ } *input = (struct INPUT *) (ncprequest); int result = nw_scan_a_root_dir( responsedata, input->dir_handle); if (result > -1) data_len = result; else completition = (uint8) (-result); } break; case 0x20 : { /* Scan Volume's User Disk Restrictions */ /* * NCP 0x2222/22/32 Scan Volume's User Disk Restrictions. * * WebSDK / headers: * Request: Volume Number, Sequence. Sequence starts at * zero and is incremented by the number of entries returned * by previous calls. * Reply: Number Of Entries followed by up to twelve * Object ID / Restriction pairs. Restrictions are reported * in 4K blocks. * * The Rust nwserver and lwared implementations both return * an empty list after validating the volume. MARS-NWE keeps * that SDK-compatible unrestricted result for non-quota * builds, and fills entries from the existing Linux quota * backend when QUOTA_SUPPORT is enabled and a restricted * bindery user maps to a Unix uid. */ uint8 volnr = *(p+1); uint32 sequence = GET_BE32(p+2); int result = scan_volume_user_disk_restrictions( volnr, sequence, responsedata); if (result > -1) data_len = result; else completition = (uint8) (-result); } break; case 0x21 : { /* change Vol restrictions for Obj */ XDPRINTF((5, 0, "Change vol restrictions")); } return(-2); /* nwbind must do prehandling */ case 0x22 : { /* remove Vol restrictions for Obj */ XDPRINTF((5, 0, "Remove vol restrictions")); } return(-2); /* nwbind must do prehandling */ case 0x25 : { /* Set Entry, Set Directory File Information * sets or changes the file or directory information to the * values entered in 'Change Bits'. * NO REPLY * used by ncopy.exe, flag.exe */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; uint8 attrib; NW_SET_DIR_INFO f; } *input = (struct INPUT *) (ncprequest); int result = nw_set_a_directory_entry( input->dir_handle, input->f.u.f.name, input->f.u.f.namlen, input->attrib, GET_BE32(input->f.searchsequence), &(input->f)); if (result < 0) { completition = (uint8)(-result); XDPRINTF((5,0, "NCP22/25 SetDirFileInfo: dh=%d attr=0x%02x seq=0x%08lx result=%d cc=0x%02x", (int)input->dir_handle, (int)input->attrib, (unsigned long)GET_BE32(input->f.searchsequence), result, (unsigned int)completition)); } } break; case 0x26 : { /* Scan file or Dir for ext trustees */ int sequence = (int)*(p+2); /* trustee sequence */ struct XDATA { uint8 entries; uint8 ids[80]; /* 20 id's */ uint8 trustees[40]; /* 20 trustees's */ } *xdata = (struct XDATA*) responsedata; uint32 ids[20]; int trustees[20]; int result = nw_scan_for_trustee( (int)*(p+1), /* dir handle */ sequence, p+4, /* path */ (int)*(p+3), /* pathlen */ 20, /* max entries */ ids, trustees, 1); /* extended */ if (result > -1) { int i = -1; uint8 *idsp = xdata->ids; uint8 *trp = xdata->trustees; memset(xdata, 0, sizeof(*xdata)); xdata->entries = result; while(++i < result) { int ncp22_rights = trustee_v3_to_ncp22_rights(trustees[i]); U32_TO_BE32(ids[i], idsp); idsp+=4; U16_TO_16(ncp22_rights, trp); /* LO - HI */ trp+=2; } data_len = sizeof(struct XDATA); } else completition = (uint8) (-result); } break; case 0x27 : { /* Add Ext Trustees to DIR or File */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Handle */ uint8 trustee_id[4]; /* Trustee Object ID */ uint8 trustee_rights[2]; /* lo - hi */ uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); uint32 trustee_id = GET_BE32(input->trustee_id); int ncp22_rights = GET_16(input->trustee_rights); int trustee_rights = trustee_ncp22_to_v3_rights(ncp22_rights); int copylen = input->pathlen; uint8 pathbuf[256]; int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff); int result; ncp23_debug_dump("NCP22/27 SetTrustee raw", (uint8 *)&input->dir_handle, raw_len); if (copylen > 255) copylen = 255; memcpy(pathbuf, input->path, copylen); pathbuf[copylen] = '\0'; result = nw_add_trustee( input->dir_handle, input->path, input->pathlen, trustee_id, trustee_rights, 1); /* extended */ XDPRINTF((5,0, "NCP22/27 SetTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx ncp22=0x%04x rights=0x%03x rc=%d", input->dir_handle, input->pathlen, pathbuf, (unsigned long)trustee_id, ncp22_rights, trustee_rights, result)); if (result) completition = (uint8) -result; } break; case 0x28 : { /* Scan File Physical ??? */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* directory handle */ uint8 attrib; /* Search Attrib ?? 0x2f */ uint8 searchsequence[4]; /* 32 bit */ uint8 len; uint8 data[2]; } *input = (struct INPUT *) (ncprequest); /* we try, whether this is ok ????? */ int result = nw_scan_a_directory( responsedata, input->dir_handle, input->data, input->len, input->attrib, GET_BE32(input->searchsequence)); if (result > -1) data_len = result; else completition = (uint8) (-result); } break; /* * NCP 0x2222/22/41 Get Object Disk Usage And Restrictions. * * WebSDK / headers: * Request: Volume Number, Object ID (high-low) * Reply: Restriction and In Use, both 32-bit values in 4K * blocks. * * A restriction value of 0x40000000 means that the object has no * disk restriction. NetWare also treats unknown/invalid object * IDs as a successful unrestricted/no-use result. * * With QUOTA_SUPPORT enabled this call needs bindery prehandling: * nwbind maps the Object ID to a Unix uid before * handle_after_bind() calls nw_get_vol_restrictions(). Without * quota support, keep the SDK-compatible fallback local so builds * on hosts without quota support do not need the quota backend. */ case 0x29 : { /* Get Object Disk Usage And Restrictions */ #if QUOTA_SUPPORT XDPRINTF((5, 0, "Read vol restrictions")); return(-2); /* nwbind must do prehandling */ #else #if DO_DEBUG uint8 volnr = *(p+1); uint32 id = GET_BE32(p+2); #endif struct XDATA { uint8 restriction[4]; uint8 inuse[4]; } *xdata = (struct XDATA*) responsedata; XDPRINTF((5,0, "Get vol restriction fallback vol=%d, id=0x%lx", (int)volnr, id)); U32_TO_32(0x40000000, xdata->restriction); U32_TO_32(0x0, xdata->inuse); data_len=sizeof(struct XDATA); #endif } break; case 0x2a : { /* Get Eff. Rights of DIR's and Files */ struct XDATA { uint8 eff_rights[2]; /* LO-HI */ } *xdata = (struct XDATA*) responsedata; int dir_handle = (int)*(p+1); int pathlen = (int)*(p+2); uint8 *path = p+3; int copylen = pathlen; uint8 pathbuf[256]; int result; if (copylen > 255) copylen = 255; memcpy(pathbuf, path, copylen); pathbuf[copylen] = '\0'; result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1); if (result > -1){ int ncp22_rights = trustee_v3_to_ncp22_rights(result); U16_TO_16(ncp22_rights, xdata->eff_rights); data_len = sizeof(struct XDATA); XDPRINTF((5,0, "NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rights=0x%04x ncp22=0x%04x", dir_handle, pathlen, pathbuf, result, ncp22_rights)); } else { completition = (uint8) (-result); XDPRINTF((5,0, "NCP22/42 GetEffectiveRights: dh=%d pathlen=%d path=`%s` rc=%d", dir_handle, pathlen, pathbuf, result)); } } break; case 0x2b : { /* remove ext trustees */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 dir_handle; /* Handle */ uint8 trustee_id[4]; /* Trustee Object ID */ uint8 reserved; uint8 pathlen; uint8 path[2]; } *input = (struct INPUT *) (ncprequest); uint32 trustee_id = GET_BE32(input->trustee_id); int copylen = input->pathlen; uint8 pathbuf[256]; int raw_len = requestlen - (int)((uint8 *)&input->dir_handle - readbuff); int result; ncp23_debug_dump("NCP22/2B DelTrustee raw", (uint8 *)&input->dir_handle, raw_len); if (copylen > 255) copylen = 255; memcpy(pathbuf, input->path, copylen); pathbuf[copylen] = '\0'; result = nw_del_trustee( input->dir_handle, input->path, input->pathlen, trustee_id, 1); /* extended */ XDPRINTF((5,0, "NCP22/2B DelTrustee: dh=%d pathlen=%d path=`%s` obj=0x%08lx rc=%d", input->dir_handle, input->pathlen, pathbuf, (unsigned long)trustee_id, result)); if (result) completition = (uint8) -result; } break; case 0x2c : { /* Get Volume and Purge Information */ /* new Call since V3.11 */ /* ncpfs need this call */ int volume = (int) *(p+1); struct XDATA { uint8 total_blocks[4]; /* LOW-HI !! */ uint8 avail_blocks[4]; uint8 purgeable_blocks[4]; uint8 not_purgeable_blocks[4]; uint8 total_dirs[4]; uint8 avail_dirs[4]; uint8 reserved_by_novell[4]; uint8 sec_per_block; uint8 namlen; uint8 name[1]; } *xdata = (struct XDATA*) responsedata; uint8 name[100]; int result = nw_get_volume_name(volume, name, sizeof(name)); if (result > -1){ struct fs_usage fsp; memset(xdata, 0, sizeof(struct XDATA)); if (!nw_get_fs_usage(name, &fsp, 0)) { xdata->sec_per_block = 8; /* hard coded */ U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks); U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks); U32_TO_32(fsp.fsu_files, xdata->total_dirs); U32_TO_32(fsp.fsu_ffree, xdata->avail_dirs); } xdata->namlen = strlen((char*)name); strmaxcpy(xdata->name, name, xdata->namlen); data_len = xdata->namlen + 30; } else completition = (uint8) -result; } break; case 0x2d : { /* Get Direktory Information */ int dir_handle = (int) *(p+1); struct XDATA { uint8 total_blocks[4]; uint8 avail_blocks[4]; uint8 total_dirs[4]; uint8 avail_dirs[4]; uint8 reserved_by_novell[4]; uint8 sec_per_block; uint8 namlen; uint8 name[1]; /* Volume Name */ } *xdata = (struct XDATA*) responsedata; int result = nw_get_vol_number(dir_handle); uint8 name[100]; if (result > -1) result = nw_get_volume_name(result, name, sizeof(name)); if (result > -1) { struct fs_usage fsp; memset(xdata, 0, sizeof(struct XDATA)); if (!nw_get_fs_usage(name, &fsp, 0)) { xdata->sec_per_block = 8; /* hard coded */ U32_TO_32(fsp.fsu_blocks/8, xdata->total_blocks); U32_TO_32(fsp.fsu_bavail/8, xdata->avail_blocks); U32_TO_32(fsp.fsu_files, xdata->total_dirs); U32_TO_32(fsp.fsu_ffree, xdata->avail_dirs); } xdata->namlen = strlen((char*)name); strmaxcpy(xdata->name, name, xdata->namlen); data_len = xdata->namlen + 22; } else completition = (uint8) -result; } break; case 0x2e : { /* Rename or Move (old) */ /* * NCP 22 / subfunction 46 (0x2e): Rename Or Move (old) * * Request: * byte source directory handle * byte search attributes * byte source path component count * source path components: byte len + bytes, repeated * byte destination directory handle * byte destination path component count * destination path components: byte len + bytes, repeated * * No reply data. This old call uses component-counted * paths, unlike the plain length-prefixed NCP 69 rename. */ uint8 srcpath[256]; uint8 dstpath[256]; int src_handle = (int)*(p+1); int searchattr = (int)*(p+2); int src_count = (int)*(p+3); int used = 0; int srclen; int dstlen; int result; uint8 *q; srclen = ncp22_component_path_to_dos(p+4, src_count, srcpath, sizeof(srcpath), &used); if (srclen < 0) { completition = (uint8)(-srclen); break; } q = p + 4 + used; dstlen = ncp22_component_path_to_dos(q+2, (int)*(q+1), dstpath, sizeof(dstpath), NULL); if (dstlen < 0) { completition = (uint8)(-dstlen); break; } XDPRINTF((5,0, "NCP22/2E RenameOrMove: src_h=%d dst_h=%d attr=0x%02x src=`%s` dst=`%s`", src_handle, (int)*q, searchattr, srcpath, dstpath)); result = nw_mv_files(searchattr, src_handle, srcpath, srclen, (int)*q, dstpath, dstlen); XDPRINTF((5,0, "NCP22/2E RenameOrMove: file_result=%d", result)); /* * nw_mv_files() is correct for file rename/move. For a * directory source it may return either Invalid Path (0x9c) * or "not found" (0xff), depending on the search attribute * shape. Directories need the directory mover, and * NCP22/2E can provide different source/destination handles. */ if (result == -0x9c || result == -0xff) { int dir_result = nw_mv_dir_between_handles(src_handle, srcpath, srclen, (int)*q, dstpath, dstlen); XDPRINTF((5,0, "NCP22/2E RenameOrMove: directory fallback result=%d", dir_result)); result = dir_result; } if (result < 0) completition = (uint8)(-result); } break; #if WITH_NAME_SPACE_CALLS case 0x2f : { /* Fill namespace buffer */ /* ncopy use this call */ int volume = (int) *(p+1); /* (p+2) == 0xe4 or 0xe2 sometimes ???? */ int result=fill_namespace_buffer( volume, responsedata); if (result > -1) { data_len = result; } else completition = (uint8) -result; } break; case 0x30 : { /* Get Name Space Directory Entry */ int volume = (int) *(p+1); uint32 basehandle = GET_32(p+2); int namespace = (int) *(p+6); int result=get_namespace_dir_entry( volume, basehandle, namespace, responsedata); if (result > -1) { data_len = result; } else completition = (uint8) -result; } break; #endif case 0x32 : { /* Get Object Effective Rights */ /* * Client32/NWCalls export: * NWNCP22s50GetObjEffectRights * * Observed request layout from Client32/NCPWIN32: * byte subfunction 0x32 * dword object id (big endian) * byte directory handle * byte path length * byte[] path * * Reply: * word effective rights mask, low/high. * * mars_nwe's trustee engine currently computes effective * rights for the active connection object. For the normal * NWGetEffectiveRights/NWGetObjectEffectiveRights use from * a logged-in client this object id is the current user. */ struct XDATA { uint8 eff_rights[2]; /* LO-HI */ } *xdata = (struct XDATA*) responsedata; uint32 object_id = GET_BE32(p+1); int dir_handle = (int)*(p+5); int pathlen = (int)*(p+6); uint8 *path = p+7; int result; if (object_id && object_id != (uint32)act_obj_id) { int save_act_obj_id = act_obj_id; /* * Best effort for explicit-object callers. Group * membership is still the connection's group list, which * is correct for the usual current-user call and avoids * changing bindery/group state in this low-level handler. */ act_obj_id = (int)object_id; result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1); act_obj_id = save_act_obj_id; } else { result = nw_get_eff_dir_rights(dir_handle, path, pathlen, 1); } if (result > -1) { int ncp22_rights = trustee_v3_to_ncp22_rights(result); U16_TO_16(ncp22_rights, xdata->eff_rights); data_len = sizeof(struct XDATA); } else completition = (uint8)(-result); } break; case 0x33 : { /* Get Extended Volume Information */ /* * NCP 0x2222/22/51 Get Extended Volume Information * * WebSDK / nwvol.h context: * Request: * byte VolumeNumber * Reply: * word VolInfoReplyLen (Lo-Hi) * NWVolExtendedInfo VolumeInfo (VolInfoReplyLen bytes) * byte VolNameLen * byte VolumeName[VolNameLen] * * NWVolExtendedInfo contains 33 little-endian 32-bit fields: * volume type, status flags, sector/cluster geometry, total and * free cluster counts, suballocation/limbo/compression/migration * counters, directory-entry counters, EA counters, a Directory * Services object id, and the last-modified date/time. * * MARS-NWE maps the Unix filesystem data we already expose via * the older volume-information calls to the core geometry and * capacity fields. NetWare-specific suballocation, limbo, * compression, migration, EA, and DS fields are reported as zero. */ struct XDATA { uint8 vol_info_reply_len[2]; uint8 vol_type[4]; uint8 status_flag[4]; uint8 sector_size[4]; uint8 sectors_per_cluster[4]; uint8 vol_size_in_clusters[4]; uint8 free_clusters[4]; uint8 sub_alloc_freeable_clusters[4]; uint8 freeable_limbo_sectors[4]; uint8 nonfreeable_limbo_sectors[4]; uint8 avail_sub_alloc_sectors[4]; uint8 nonuseable_sub_alloc_sectors[4]; uint8 sub_alloc_clusters[4]; uint8 num_data_streams[4]; uint8 num_limbo_data_streams[4]; uint8 oldest_del_file_age_in_ticks[4]; uint8 num_compressed_data_streams[4]; uint8 num_compressed_limbo_data_streams[4]; uint8 num_noncompressible_data_streams[4]; uint8 precompressed_sectors[4]; uint8 compressed_sectors[4]; uint8 num_migrated_data_streams[4]; uint8 migrated_sectors[4]; uint8 clusters_used_by_fat[4]; uint8 clusters_used_by_dirs[4]; uint8 clusters_used_by_ext_dirs[4]; uint8 total_dir_entries[4]; uint8 unused_dir_entries[4]; uint8 total_ext_dir_extants[4]; uint8 unused_ext_dir_extants[4]; uint8 ext_attrs_defined[4]; uint8 ext_attr_extants_used[4]; uint8 directory_services_object_id[4]; uint8 vol_last_modified_date_and_time[4]; uint8 vol_name_len; uint8 vol_name[16]; } *xdata = (struct XDATA*) responsedata; uint8 name[100]; int volume = (int) *(p+1); int result = nw_get_volume_name(volume, name, sizeof(name)); memset(xdata, 0, sizeof(struct XDATA)); if (result > -1) { struct fs_usage fsp; int namlen = strlen((char*)name); if (namlen > 16) namlen = 16; U16_TO_16(33 * 4, xdata->vol_info_reply_len); U32_TO_32(3, xdata->vol_type); /* VINetWare386v31 */ U32_TO_32(512, xdata->sector_size); U32_TO_32(8, xdata->sectors_per_cluster); /* 4 KiB clusters */ if (!nw_get_fs_usage(name, &fsp, 0)) { U32_TO_32(fsp.fsu_blocks/8, xdata->vol_size_in_clusters); U32_TO_32(fsp.fsu_bavail/8, xdata->free_clusters); U32_TO_32(fsp.fsu_files, xdata->total_dir_entries); U32_TO_32(fsp.fsu_ffree, xdata->unused_dir_entries); } xdata->vol_name_len = namlen; strmaxcpy(xdata->vol_name, name, namlen); data_len = 2 + (33 * 4) + 1 + namlen; XDPRINTF((5,0, "NCP22/33 GetExtendedVolumeInfo: vol=%d name=`%s` len=%d", volume, name, data_len)); } else { completition = (uint8) -result; } } break; default: completition = 0xfb; /* unkwown request */ break; } /* switch *p */ } break; case 0x17 : { /* FILE SERVER ENVIRONMENT */ /* uint8 len = *(requestdata+1); */ uint8 ufunc = *(requestdata+2); uint8 *rdata = requestdata+3; switch (ufunc) { #if FUNC_17_02_IS_DEBUG case 0x02 : { /* I hope this call isn't used */ /* now missused as a debug switch :) */ struct XDATA { uint8 nw_debug; /* old level */ } *xdata = (struct XDATA*) responsedata; if (*rdata == NWCONN) { xdata->nw_debug = (uint8)org_nw_debug; nw_debug = org_nw_debug = (int) *(rdata+1); data_len = 1; } else return(-1); } break; #endif case 0x14: /* Login Objekt, unencrypted passwords */ case 0x18: /* crypt_keyed LOGIN */ return(-2); /* nwbind must do prehandling */ case 0x0f: { /* Scan File Information */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0, len + ufunc */ uint8 sequence[2]; /* z.B. 0xff, 0xff */ uint8 dir_handle; uint8 search_attrib; /* 0: NONE */ /* 02: HIDDEN */ /* 04: SYSTEM */ /* 06: BOTH */ /* 0x10: DIR */ uint8 len; uint8 data[2]; /* Name */ } *input = (struct INPUT *)ncprequest; struct XDATA { uint8 sequence[2]; /* next sequence */ /* NW_FILE_INFO f; */ uint8 f[sizeof(NW_FILE_INFO)]; uint8 owner_id[4]; uint8 archive_date[2]; uint8 archive_time[2]; uint8 reserved[56]; } *xdata = (struct XDATA*)responsedata; int len = input->len; int searchsequence; NW_FILE_INFO f; uint32 owner; memset(xdata, 0, sizeof(struct XDATA)); searchsequence = nw_search( (uint8*) &f, &owner, (int)input->dir_handle, (int) GET_BE16(input->sequence), (int) input->search_attrib & ~0x10, /* this routine is only for scanning files ^^^^^^ */ input->data, len); if (searchsequence > -1) { memcpy(xdata->f, &f, sizeof(NW_FILE_INFO)); U16_TO_BE16((uint16) searchsequence, xdata->sequence); U32_TO_BE32(owner, xdata->owner_id); data_len = sizeof(struct XDATA); } else completition = (uint8) (- searchsequence); } break; case 0x10: { /* Set File Information */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0, len + ufunc */ uint8 f[sizeof(NW_FILE_INFO) - 14]; /* no name */ uint8 owner_id[4]; uint8 archive_date[2]; uint8 archive_time[2]; uint8 reserved[56]; uint8 dir_handle; uint8 search_attrib; /* 0: NONE */ /* 02: HIDDEN */ /* 04: SYSTEM */ /* 06: BOTH */ /* 0x10: DIR */ uint8 len; uint8 data[2]; /* Name */ } *input = (struct INPUT *)ncprequest; NW_FILE_INFO f; int result; memcpy(((uint8*)&f)+14, input->f, sizeof(NW_FILE_INFO)-14); result = nw_set_file_information((int)input->dir_handle, input->data, (int)input->len, (int)input->search_attrib, &f); /* no reply packet */ if (result <0) completition = (uint8)-result; } break; case 0x47 : { /* SCAN BINDERY OBJECT TRUSTEE PATH */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 div[3]; /* 0x0, dlen, ufunc */ uint8 volume; uint8 sequence[2]; /* trustee searchsequence */ uint8 id[4]; /* Trustee Object ID */ } *input = (struct INPUT *) (ncprequest); struct XDATA { uint8 nextsequence[2]; uint8 id[4]; uint8 access_mask; uint8 pathlen; uint8 path[1]; } *xdata = (struct XDATA*) responsedata; int sequence=GET_BE16(input->sequence); int access_mask=0; uint32 id=GET_BE32(input->id); int result=nw_scan_user_trustee( input->volume, &sequence, id, &access_mask, xdata->path); if (result > 0) { U16_TO_BE16(sequence, xdata->nextsequence); memcpy(xdata->id, input->id, 4); xdata->access_mask=(uint8)access_mask; xdata->pathlen=result; data_len = 8+result; } else if (!result) { memset(xdata, 0, 8); data_len = 8; } else completition = (uint8)(-result); } break; case 0x64: { /* create queue */ #if 0 int q_typ = GET_BE16(rdata); #endif int q_name_len = *(rdata+2); #if 0 uint8 *q_name = rdata+3; #endif uint8 *dirhandle = rdata+3+q_name_len; int pathlen = *(rdata+3+q_name_len+1); uint8 *path = rdata+3+q_name_len+2; uint8 new_path[257]; int result = conn_get_full_path(*dirhandle, path, pathlen, new_path, sizeof(new_path)); if (result > -1) { int diffsize = result - pathlen; *dirhandle = 0; memcpy(path, new_path, result); if (diffsize) requestlen+=diffsize; /* !!!!!! */ return(-1); /* nwbind must do the rest */ } else completition = (uint8)(-result); } break; case 0x68: /* create queue job and file old */ case 0x79: /* create queue job and file */ return(-2); /* nwbind must do prehandling */ case 0x6C: /* Get Queue Job Entry old */ case 0x7A: { /* Read Queue Job Entry */ uint32 q_id = GET_BE32(rdata); int job_id = GET_BE16(rdata+4); uint32 fhandle = get_queue_job_fhandle(q_id, job_id); U32_TO_BE32(fhandle, rdata+8); requestlen+=6; /* !!!!!! */ } return(-1); /* nwbind must do the rest */ case 0x69: /* close file and start queue old ?? */ case 0x7f: { /* close file and start queue */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 packetlen[2]; /* lo - hi */ uint8 func; /* 0x7f or 0x69 */ uint8 queue_id[4]; /* Queue ID */ uint8 job_id[4]; /* result from creat queue */ /* if 0x69 then only first 2 byte ! */ } *input = (struct INPUT *) (ncprequest); uint32 q_id = GET_BE32(input->queue_id); int job_id = (ufunc==0x69) ? GET_BE16(input->job_id) : GET_BE16(input->job_id); int result = close_queue_job(q_id, job_id); if (result < 0) { completition = (uint8)-result; } else { return(-2); /* nwbind must do next */ } } break; case 0x71 : /* service queue job (old) */ case 0x7c : /* service queue job */ return(-2); /* nwbind must do prehandling */ case 0x72 : /* finish queue job (old) */ case 0x73 : /* abort queue job (old) */ case 0x83 : /* finish queue job */ case 0x84 : { /* abort queue job */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 packetlen[2]; /* low high */ uint8 func; /* 0x7f or 0x69 */ uint8 queue_id[4]; /* Queue ID */ uint8 job_id[4]; /* result from creat queue */ /* if 0x69 then only first 2 byte ! */ } *input = (struct INPUT *) (ncprequest); uint32 q_id = GET_BE32(input->queue_id); int job_id = GET_BE16(input->job_id); int result = finish_abort_queue_job(q_id, job_id); if (result <0) completition=(uint8) -result; else return(-1); /* nwbind must do the rest */ } break; case 0xf3: { /* Map Directory Number TO PATH */ int payload_len = requestlen - 3; int result; if (payload_len < 0) payload_len = 0; ncp23_debug_dump("NCP23/F3 Map Directory Number TO PATH", rdata, payload_len); /* * Request: volume byte, directory number dword LO-HI, namespace byte. * Reply: length-preceded path components, no volume component. */ if (payload_len >= 6) { int volume = (int)rdata[0]; uint32 dirnum = GET_32(rdata+1); int namspace = (int)rdata[5]; result = map_directory_number_to_path(volume, dirnum, namspace, responsedata, sizeof(IPX_DATA) - sizeof(NCPRESPONSE)); if (result > -1) data_len = result; else completition = (uint8)(-result); } else { completition = 0x9c; } } break; case 0xf4: { /* Map PATH TO Dir Entry */ int payload_len = requestlen - 3; int result; if (payload_len < 0) payload_len = 0; ncp23_debug_dump("NCP23/F4 Map PATH TO Dir Entry", rdata, payload_len); /* * Request: dir handle byte, path length byte, path bytes. * Reply: volume byte, directory number dword LO-HI. */ if (payload_len >= 2 && payload_len >= 2 + (int)rdata[1]) { struct XDATA { uint8 volume; uint8 dirnum[4]; } *xdata = (struct XDATA*)responsedata; result = conn_map_path_to_dir_entry((int)rdata[0], rdata+2, (int)rdata[1], &(xdata->volume), xdata->dirnum); if (result) completition = (uint8)(-result); else data_len = sizeof(struct XDATA); } else { completition = 0x9c; } } break; default : return(-1); break; } /* switch (ufunc) */ } /* case 0x17 */ break; case 0x18 : /* End of Job */ if (!(entry8_flags&0x200)) /* pcz: 14-Apr-00 */ free_connection_task_jobs(ncprequest->task); nw_free_handles(ncprequest->task); return(-1); /* nwbind must do a little rest */ break; case 0x19 : /* logout, some of this call is handled in ncpserv. */ free_queue_jobs(); nw_free_handles(-1); set_nw_user(-1, -1, 0, 0, NULL, -1, NULL, 0, NULL); return(-1); /* nwbind must do a little rest */ break; case 0x1a : /* Log Physical Record */ case 0x1c : /* Release Physical Record */ case 0x1e : /* Clear Physical Record */ { /* * SDK: NCP 0x2222/26 Log Physical Record records one * byte range in the caller's logged data block table. * Lock Flag 0 logs for future locking, 1 locks * exclusive, and 3 locks shareable/read-only. * * SDK: NCP 0x2222/28 Release Physical Record releases * one locked byte range but leaves it in the logged * table so a later Lock Physical Record Set can relock * it. The request carries a reserved byte, a 6-byte * NetWare file handle, Start Offset, and Record Length; * it returns no reply data and reports Unlock Error * through the completion code. * * SDK: NCP 0x2222/30 Clear Physical Record releases the * range if it is locked and removes it from the logged * table. * * The physical-record set table is maintained alongside * the existing low-level byte-range lock implementation: * log/lock records are added on successful 0x1a calls, * release leaves the set entry intact, and clear removes * the matching set entry after a successful unlock. */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 lock_flag; /* 0=log/reserved, 1=excl */ /* 3=shared */ uint8 ext_fhandle[2]; /* all zero / high handle */ uint8 fhandle[4]; /* Filehandle */ uint8 offset[4]; uint8 size[4]; uint8 timeout[2]; } *input = (struct INPUT *)ncprequest; int fhandle = GET_32 (input->fhandle); uint32 offset= GET_BE32(input->offset); uint32 size = GET_BE32(input->size); uint16 timeout = GET_BE16(input->timeout); int result; if (function == 0x1a) { /* log or lock */ result = nw_log_physical_record( fhandle, offset, size, timeout, (int)input->lock_flag); if (!result) result = share_set_physrec_add_rm( (int)input->lock_flag, fhandle, offset, size, timeout); } else { result = nw_log_physical_record( fhandle, offset, size, timeout, (function == 0x1c) ? -1 /* unlock only */ : -2 /* unlock + unlog */ ); if (!result && function == 0x1e) result = share_set_physrec_add_rm( -2, fhandle, offset, size, timeout); } if (result) completition = (uint8)-result; } break; case 0x1b : /* Lock Physical Record Set (old) */ case 0x6e : { /* Lock Physical Record Set */ /* * SDK: NCP 0x2222/27 Lock Physical Record Set (old) * and NCP 0x2222/110 Lock Physical Record Set lock all * byte ranges logged by the calling client. * * SDK request: one-byte Lock Flag followed by a 2-byte * Lock Timeout in 1/18 second units. Lock Flag bit 1 * selects shareable/read-only locks; otherwise the set is * locked exclusive/read-write. The reply carries no data * and completion reports success, timeout, or lock error. * * MARS-NWE stores physical-record set entries from Log * Physical Record and routes the set lock through the * shared synchronization-set handler. */ struct INPUT { uint8 header[7]; uint8 lock_flag; uint8 timeout[2]; } *input = (struct INPUT *)ncprequest; int lock_flag = (input->lock_flag & 0x02) ? 3 : 1; int result = share_handle_lock_sets( 4, /* Physical Record Set */ lock_flag, GET_BE16(input->timeout)); if (result) completition = (uint8)-result; } break; case 0x1d : /* Release Physical Record Set */ case 0x1f : { /* Clear Physical Record Set */ /* * SDK: NCP 0x2222/29 Release Physical Record Set * releases all byte ranges locked by the calling client * but leaves them in the client's data byte range table * for future Lock Physical Record Set calls. * * SDK: NCP 0x2222/31 Clear Physical Record Set releases * all locked ranges and clears the caller's data byte * range table. Both calls have no reply data; Clear * carries a one-byte Lock Flags field documented as * 0 = Not Locked. */ int result = share_handle_lock_sets( 4, /* Physical Record Set */ (function==0x1f) ? -2 /* Clear */ : -1, /* Release */ 0); if (result) completition = (uint8)-result; } break; case 0x20 : /* Semaphore */ return(-1); /* handled by nwbind */ case 0x21 : { /* Negotiate Buffer Size, Packetsize */ uint8 *getsize=responsedata; int buffer_size = (int) (GET_BE16((uint8*)requestdata)); /* Der Novell-Client der PAM's Net/E-Ethernetkarte f�r Atari ST/TT meldet ein Packetsize von 0 wenn nwserv NACH dem Novell Client NET_S1.PRG gestartet wird. Da 0 in jedem Falle ein unsinniger Wert ist, wird rw_buffer_size nicht verwendet. Hayo Schmidt <100305.1424@compuserve.com>, 7-Dec-97 */ if (buffer_size >= 512) { rw_buffer_size = min(LOC_RW_BUFFERSIZE, buffer_size); XDPRINTF((3,0, "Negotiate Buffer size = 0x%04x,(%d)", (int) rw_buffer_size, (int) rw_buffer_size)); } else { XDPRINTF((1,0, "Invalid Packetsize = %d, " "Negotiate Buffer Size is set to %d", buffer_size, rw_buffer_size)); } U16_TO_BE16(rw_buffer_size, getsize); data_len = 2; } break; case 0x22 : { /* Transaction Tracking System (TTS) calls */ int ufunc = (int) *requestdata; /* * WebSDK / headers: * NCP 0x2222/34/00 TTS Is Available has no reply data. * Its completion code is the status value: 0x00 means * Transaction Tracking Unavailable, 0xfd means Disabled, * and 0xff means Available. The SDK headers expose this * as NWTTSIsAvailable(). * * MARS-NWE does not implement transaction tracking. Report * the documented unavailable status for the availability * probe, but keep the state-changing TTS subfunctions * unsupported rather than pretending to begin, end, or abort * transactions without rollback semantics. * * Cross-check: lwared and the Rust nwserver code do not * provide a fuller TTS transaction implementation to mirror. */ if (!ufunc) completition=0; /* TTS unavailable */ else completition=0xfb; /* request not known */ } break; case 0x23 : { /* div AFP Calls */ int afp_off = afp_request_offset(); uint8 *afp_req = requestdata + afp_off; int afp_len = requestlen - afp_off; int ufunc = (afp_len > 0) ? (int)*afp_req : -1; /* * NetWare AFP calls are server-side NCP entry points for * Mac namespace semantics: AFP entry IDs, Finder Info, * AppleDouble metadata, resource forks, and per-volume Mac * namespace state. * * WebSDK / headers identify the old NCP 0x2222/35 AFP * subfunctions used by nwafp.h, including 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, "Packet Burst negotiate buffer: want=0x%04x reply=0x%04x rw_buffer=0x%04x echo_socket=0x%04x flags=0x%x", (int) GET_BE16((uint8*)requestdata), (int) wantsize, (int) rw_buffer_size, (int) sock_echo, flags)); } else #endif { XDPRINTF((2,0, "Function '0x61' (Burst) not enabled")); completition = 0xfb; /* unknown request */ nw_debug=0; } break; case 0x65 : /* Packet Burst Connection Request */ #if ENABLE_BURSTMODE if (server_version_flags&1) { /* enable burstmode */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 connid[4]; /* RANDOM ID */ /* build by time() */ uint8 max_packet_size[4]; /* HI-LO */ /* max_packet_size is here max. * phys. packet size without MAC-header * e.g. 1500 if ethernet */ uint8 target_socket[2]; /* HI-LO */ uint8 max_send_size[4]; /* HI-LO */ uint8 max_recv_size[4]; /* HI-LO */ } *input = (struct INPUT *)ncprequest; struct XDATA { uint8 server_id[4]; /* RANDOM ID */ /* build by time() */ uint8 max_packet_size[4]; /* HI-LO */ uint8 max_send_size[4]; /* HI-LO */ uint8 max_recv_size[4]; /* HI-LO */ } *xdata= (struct XDATA*) responsedata; int client_socket=GET_BE16(input->target_socket); uint32 max_packet_size=min(sizeof(IPX_DATA), GET_BE32(input->max_packet_size)-30); U32_TO_BE32(max_packet_size + 30, xdata->max_packet_size); if (!burst_w) burst_w=(BURST_W*)xcmalloc(sizeof(BURST_W)); xfree(burst_w->sendburst); xfree(burst_w->send_buf); xfree(burst_w->recv_buf); burst_w->max_burst_data_size= max_packet_size-sizeof(BURSTPACKET); burst_w->sendburst= (BURSTPACKET*)xcmalloc(max_packet_size); burst_w->ud.udata.buf = (char*)(burst_w->sendburst); burst_w->sendburst->type[0]=0x77; burst_w->sendburst->type[1]=0x77; burst_w->sendburst->streamtyp=2; /* BIG_SEND_BURST */ U32_TO_BE32(time(NULL), burst_w->sendburst->source_conn); U16_TO_16(act_connection, burst_w->sendburst->source_conn); /* we need to identify it */ memcpy(xdata->server_id, burst_w->sendburst->source_conn, 4); memcpy(burst_w->sendburst->dest_conn, input->connid, 4); burst_w->max_send_size= min(max_burst_send_size, GET_BE32(input->max_recv_size)); burst_w->send_buf=xcmalloc(burst_w->max_send_size+8); burst_w->max_recv_size= min(max_burst_recv_size, GET_BE32(input->max_send_size)); #if 1 /* MUST BE REMOVED LATER !!! */ /* we don't want fragmented receive packets */ if (burst_w->max_recv_size > burst_w->max_burst_data_size-24) burst_w->max_recv_size =burst_w->max_burst_data_size-24; #endif burst_w->recv_buf=xcmalloc(burst_w->max_recv_size+24); #if 1 U32_TO_BE32(0x5ff22, burst_w->sendburst->delaytime); #endif U32_TO_BE32(burst_w->max_recv_size, xdata->max_recv_size); U32_TO_BE32(burst_w->max_send_size, xdata->max_send_size); burst_w->ipx_pack_typ = PACKT_CORE; burst_w->ud.opt.len = sizeof(uint8); burst_w->ud.opt.maxlen = sizeof(uint8); burst_w->ud.opt.buf = (char*)&(burst_w->ipx_pack_typ); memcpy(&(burst_w->to_addr), &from_addr, sizeof(ipxAddr_t)); U16_TO_BE16(client_socket, burst_w->to_addr.sock); burst_w->ud.addr.len = sizeof(ipxAddr_t); burst_w->ud.addr.maxlen = sizeof(ipxAddr_t); burst_w->ud.addr.buf = (char*)&(burst_w->to_addr); data_len = sizeof(*xdata); XDPRINTF((2,0, "Packet Burst connection accepted: client_socket=0x%04x max_packet=%u max_data=%u send=%u recv=%u", client_socket, (unsigned)(max_packet_size + 30), (unsigned)burst_w->max_burst_data_size, (unsigned)burst_w->max_send_size, (unsigned)burst_w->max_recv_size)); } else #endif { XDPRINTF((2,0, "Packet Burst Connection Request not enabled")); nw_debug=0; completition = 0xfb; /* unknown request */ } break; case 0x68 : /* NDS NCP, NDS Fragger Protokoll ?? */ XDPRINTF((2,0, "NDS Fragger Protokoll not supportet")); nw_debug=0; completition = 0xfb; /* unknown request */ break; default : completition = 0xfb; /* unknown request */ break; } /* switch function */ } else if (ncp_type == 0x1111) { free_queue_jobs(); (void) nw_init_connect(); last_sequence = -9999; } else { XDPRINTF((1,0, "WRONG TYPE:0x%x", ncp_type)); completition = 0xfb; } if (nw_debug && (completition == 0xfb || (do_druck == 1))) { /* UNKWON FUNCTION od. TYPE */ pr_debug_request(); if (completition == 0xfb) { int req_len = requestlen - sizeof(NCPREQUEST); int subfunc = -1; if (req_len > 2 && (function == 0x15 || function == 0x16 || function == 0x17)) subfunc = requestdata[2]; else if (req_len > 0 && function == 0x20) subfunc = requestdata[0]; if (subfunc > -1) XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x ufunc=0x%x", ncp_type, function, subfunc)); else XDPRINTF((0,0, "UNKNOWN FUNCTION or TYPE: type=0x%x func=0x%x", ncp_type, function)); } else if (data_len){ int j = data_len; uint8 *p = responsedata; XDPRINTF((0,2, "RSPONSE: len %d, DATA:", data_len)); while (j--) { int c = *p++; if (c > 32 && c < 127) XDPRINTF((0,3,",\'%c\'", (char) c)); else XDPRINTF((0,3,",0x%x", c)); } XDPRINTF((0,1, NULL)); } } ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len); nw_debug = org_nw_debug; return(0); } static void handle_after_bind() { NCPREQUEST *ncprequest = (NCPREQUEST*) saved_readbuff; uint8 *requestdata = saved_readbuff + sizeof(NCPREQUEST); uint8 *bindresponse = readbuff + sizeof(NCPRESPONSE); int data_len = 0; int completition = 0; switch (ncprequest->function) { /* QUOTA support from: Matt Paley */ case 0x16 : { uint8 ufunc = *(requestdata+2); switch (ufunc) { case 0x21: { /* change Vol restrictions for Obj */ uint8 volnr = *(requestdata+3); uint32 id = GET_BE32(requestdata+4); uint32 blocks = GET_32(requestdata+8); int gid = ((int *) bindresponse)[0]; int uid = ((int *) bindresponse)[1]; int perm = ((int *) bindresponse)[2]; int result; XDPRINTF((5, 0, "Change vol rest id=%x vol=%d blocks=%d gid=%d uid=%d p=%d", (int) id, (int) volnr, (int) blocks, (int) gid, (int) uid, (int) perm)); if (perm == 0) { result = nw_set_vol_restrictions(volnr, uid, blocks); } else { result = -0x85; } if (result < 0) completition = (uint8)-result; } break; case 0x22: { /* Remove Vol restrictions for Obj */ uint8 volnr = *(requestdata+3); uint32 id = GET_BE32(requestdata+4); int gid = ((int *) bindresponse)[0]; int uid = ((int *) bindresponse)[1]; int perm = ((int *) bindresponse)[2]; int result; XDPRINTF((1, 0, "Remove vol rest id=%x vol=%d gid=%d uid=%d p=%d", (int) id, (int) volnr, (int) gid, (int) uid, (int) perm)); if (perm == 0) { result = nw_set_vol_restrictions(volnr, uid, 0); } else { result = -0x85; } if (result < 0) completition = (uint8)-result; } break; case 0x29: { /* Get Vol restrictions for Obj */ uint8 volnr = *(requestdata+3); uint32 id = GET_BE32(requestdata+4); int gid = ((int *) bindresponse)[0]; int uid = ((int *) bindresponse)[1]; int perm = ((int *) bindresponse)[2]; uint32 quota, used; int result; struct XDATA { uint8 restriction[4]; uint8 inuse[4]; } *xdata = (struct XDATA*) responsedata; XDPRINTF((5, 0, "Get vol rest id=%x vol=%d gid=%d uid=%d p=%d", (int) id, (int) volnr, (int) gid, (int) uid, (int) perm)); if (perm == 0) { result = nw_get_vol_restrictions(volnr, uid, "a, &used); } else { result = -0x85; } data_len = 8; if (result == 0) { U32_TO_32(quota, xdata->restriction); U32_TO_32(used, xdata->inuse); } else { U32_TO_32(0x40000000, xdata->restriction); U32_TO_32(0x0, xdata->inuse); completition = (uint8) -result; } } break; default : completition = 0xfb; } break; } case 0x17 : { /* FILE SERVER ENVIRONMENT */ uint8 ufunc = *(requestdata+2); switch (ufunc) { case 0x14: /* Login Objekt, unencrypted passwords */ case 0x18: { /* crypt_keyed LOGIN */ int grpcount = * (int*)(bindresponse + 4 * sizeof(int)); uint32 *grps = (uint32*)(bindresponse + 5 * sizeof(int)); int unxloginlen = (int)*(uint8*)(grps+grpcount); uint8 *unxloginname = (uint8*)(grps+grpcount)+1; uint8 objname[48]; /* ncpserv have changed the structure */ if (ufunc==0x14) { xstrmaxcpy(objname, requestdata+6, (int) *(requestdata+5)); } else if (ufunc==0x18){ xstrmaxcpy(objname, requestdata+14, (int) *(requestdata+13)); } else objname[0]='\0'; set_nw_user(*((int*)bindresponse), /* gid */ *((int*)(bindresponse+sizeof(int))), /* uid */ *((int*)(bindresponse + 2 * sizeof(int))), /* id_flags */ *((uint32*)(bindresponse + 3 * sizeof(int))), /* id */ objname, /* login name */ unxloginlen, unxloginname, grpcount, grps); } break; case 0x68: /* create queue job and file old */ case 0x79: { /* create queue job and file */ /* nwbind made prehandling */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 packetlen[2]; /* low high */ uint8 func; /* 0x79 or 0x68 */ uint8 queue_id[4]; /* Queue ID */ uint8 queue_job[280]; /* oldsize is 256 */ } *input = (struct INPUT *) (ncprequest); uint32 q_id = GET_BE32(input->queue_id); uint8 *qjob = bindresponse; int result = creat_queue_job( (int) ncprequest->task, q_id, qjob, responsedata, (ufunc == 0x68) ); if (result > -1) data_len=result; else completition = (uint8) -result; } break; case 0x69: /* close file and start queue old ?? */ case 0x7f: { /* close file and start queue */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 packetlen[2]; /* low high */ uint8 func; /* 0x7f or 0x69 */ uint8 queue_id[4]; /* Queue ID */ uint8 job_id[4]; /* result from creat queue */ /* if 0x69 then only 2 byte ! */ } *input = (struct INPUT *) (ncprequest); struct RINPUT { uint8 client_area[152]; uint8 prc_len; /* len of printcommand */ uint8 prc[1]; /* printcommand */ } *rinput = (struct RINPUT *) (bindresponse); uint32 q_id = GET_BE32(input->queue_id); int job_id = (ufunc==0x69) ? GET_BE16(input->job_id) : GET_BE16(input->job_id); int result = close_queue_job2(q_id, job_id, rinput->client_area, rinput->prc, rinput->prc_len); if (result < 0) completition = (uint8)-result; } break; case 0x71 : /* service queue job (old) */ case 0x7c : { /* service queue job */ struct INPUT { uint8 header[7]; /* Requestheader */ uint8 packetlen[2]; /* low high */ uint8 func; /* 0x7c,0x71 */ uint8 queue_id[4]; /* Queue ID */ uint8 job_typ[2]; /* service typ */ } *input = (struct INPUT *) (ncprequest); uint32 q_id = GET_BE32(input->queue_id); uint8 *qjob = bindresponse; int result = service_queue_job((int)ncprequest->task, q_id, qjob, responsedata, ufunc==0x71); if (result > -1) data_len=result; else completition = (uint8) -result; } break; default : completition = 0xfb; } } break; default : completition = 0xfb; } /* switch */ ncp_response(ncprequest->sequence, ncprequest->task, completition, data_len); } #if ENABLE_BURSTMODE static int send_burst(int offset, int datasize, int flags) { BURSTPACKET *sb=burst_w->sendburst; U32_TO_BE32(burst_w->packet_sequence++, sb->packet_sequence); U32_TO_BE32(offset, sb->burstoffset); U16_TO_BE16(datasize, sb->datasize); U16_TO_BE16(0, sb->missing); sb->flags = (uint8)flags; memcpy(sb+1, burst_w->send_buf+offset, datasize); burst_w->ud.udata.len = burst_w->ud.udata.maxlen = datasize+sizeof(BURSTPACKET); XDPRINTF((8, 0, "Packet Burst send packet: seq=%u off=%d size=%d flags=0x%x", (unsigned)(burst_w->packet_sequence-1), offset, datasize, flags)); if (t_sndudata(FD_NCP_OUT, &(burst_w->ud)) < 0){ if (nw_debug) t_error("t_sndudata in NWCONN !OK"); return(-1); } return(0); } #include static void sleep_mu(int mu) { struct timeval t; t.tv_sec = 0; t.tv_usec = mu; (void) select(1, NULL, NULL, NULL, &t); } static void handle_burst_response(uint32 offset, int size) { BURSTPACKET *sb=burst_w->sendburst; U16_TO_BE16(burst_w->burst_sequence, sb->burst_seq); U16_TO_BE16(burst_w->burst_sequence+1, sb->ack_seq); U32_TO_BE32(size, sb->burstsize); XDPRINTF((3, 0, "Packet Burst response: seq=%u offset=%u size=%d max_packet_data=%d", (unsigned)burst_w->burst_sequence, (unsigned)offset, size, burst_w->max_burst_data_size)); while (size) { int sendsize=min(size, burst_w->max_burst_data_size); int flags=0; size-=sendsize; if (!size) flags|=0x10; /* EndOfBurst */ send_burst(offset, sendsize, flags); #if 0 sleep_mu(2); #endif offset+=sendsize; } } static void handle_burst(BURSTPACKET *bp, int len) { if (burst_w) { uint32 burstoffset = GET_BE32(bp->burstoffset); int burstsequence = GET_BE16(bp->burst_seq); int datasize = GET_BE16(bp->datasize); XDPRINTF((6, 0, "Packet Burst recv packet: seq=%d flags=0x%x off=%u datasize=%d len=%d", burstsequence, bp->flags, (unsigned)burstoffset, datasize, len)); if (datasize && !(bp->flags & 0x80)) { /* copy if no System Packet */ if (datasize+burstoffset > burst_w->max_recv_size+24) { XDPRINTF((1, 0, "recv burstpacket offs=%d+size=%d > max_recv+24=%d", burstoffset, datasize, burst_w->max_recv_size+24)); return; } memcpy(burst_w->recv_buf+burstoffset, bp+1, datasize); } if (bp->flags & 0x10) { /* last packet, now action */ /* 0x10 = EOB flag */ struct REQ { uint8 function[4]; /* lo-hi 1=READ, 2=WRITE */ uint8 fhandle[4]; /* from open file */ uint8 reserved1[4]; /* all zero */ uint8 reserved2[4]; /* ??? c8,0 od. c9,f0 */ uint8 file_offset[4]; /* HI-LO */ uint8 file_size [4]; /* HI-LO */ uint8 data[2]; /* only Write */ } *req=(struct REQ*)(burst_w->recv_buf); int function=GET_32(req->function); if (function == 1 || function == 2) { /* Read or Write */ uint32 fhandle = GET_32(req->fhandle); uint32 foffset = GET_BE32(req->file_offset); uint32 fsize = GET_BE32(req->file_size); if (function == 1) { /* Read Request */ struct XDATA { uint8 resultcode[4]; /* lo-hi , * 0=noerror=OK, * 1=init-err, * 2=IO-err, * 3=no data */ uint8 readbytes[4]; /* hi-lo */ } *xdata= (struct XDATA*)burst_w->send_buf; int zusatz = 0; /* (foffset & 1) ? 1 : 0; */ int size; XDPRINTF((2, 0, "Packet Burst READ request: fh=0x%x off=%u size=%u seq=%d", (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, burstsequence)); size = nw_read_file(fhandle, burst_w->send_buf+sizeof(struct XDATA), fsize, foffset); if (zusatz) { XDPRINTF((1, 0, "foffset=%d, fsize=%d", foffset, fsize)); } if (size > -1) { U32_TO_32(0, xdata->resultcode); U32_TO_BE32(size, xdata->readbytes); XDPRINTF((2, 0, "Packet Burst READ reply: fh=0x%x off=%u requested=%u read=%d", (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size)); } else { U32_TO_32(3, xdata->resultcode); U32_TO_BE32(0, xdata->readbytes); size=0; } burst_w->burst_sequence = burstsequence; handle_burst_response(0, size+sizeof(struct XDATA)); } else { /* Write Request */ struct XDATA { uint8 resultcode[4]; /* lo-hi , * 0=noerror=OK, * 4=write error */ } *xdata= (struct XDATA*)burst_w->send_buf; int size; XDPRINTF((2, 0, "Packet Burst WRITE request: fh=0x%x off=%u size=%u seq=%d", (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, burstsequence)); size = nw_write_file(fhandle, req->data, fsize, foffset); U32_TO_32(size==fsize ? 0 : 4, xdata->resultcode); XDPRINTF((2, 0, "Packet Burst WRITE reply: fh=0x%x off=%u requested=%u written=%d result=%s", (unsigned)fhandle, (unsigned)foffset, (unsigned)fsize, size, size==fsize ? "ok" : "error")); burst_w->burst_sequence = burstsequence; handle_burst_response(0, sizeof(struct XDATA)); } } else { XDPRINTF((1, 0, "burst unknow function=0x%x", function)); } req->function[0]=0; } else if (bp->flags & 0x80) { /* System Flag */ int missing=GET_BE16(bp->missing); uint8 *p=(uint8*)(bp+1); burst_w->burst_sequence = burstsequence; XDPRINTF((3, 0, "Packet Burst missing-list request: seq=%d missing=%d", burstsequence, missing)); while (missing--){ int offs=GET_BE32(p); int size=GET_BE16(p+4); XDPRINTF((3, 0, "Packet Burst resend range: off=%d size=%d", offs, size)); handle_burst_response(offs, size); p+=6; } } } else { XDPRINTF((1, 0, "burst_w not allocated")); } } #endif static void close_all(void) { nw_exit_connect(); close(0); close(FD_NCP_OUT); } static int fl_get_int=0; /* signals * &01 sig_quit * &02 sig_hup * &04 sig_usr1 * &08 sig_usr2 */ static void sig_quit(int rsig) { XDPRINTF((2, 0, "Got Signal=%d", rsig)); fl_get_int |= 1; } static void sig_pipe(int rsig) { XDPRINTF((1, 0, "Got SIG_PIPE")); signal(SIGPIPE, sig_pipe); } static void sig_hup(int rsig) { fl_get_int |= 2; signal(SIGHUP, sig_hup); } static void sig_usr1(int rsig) { fl_get_int |= 4; } static void sig_usr2(int rsig) { fl_get_int |= 8; } static void get_new_debug(void) { get_ini_debug(3); fl_get_int &= ~2; } static void handle_extern_command(void) { fl_get_int &= ~4; signal(SIGUSR1, sig_usr1); } static void handle_sigusr2(void) { char fn[256]; FILE *f; fl_get_int &= ~8; sprintf(fn, "/tmp/nwconn%04d.log", act_connection); if (seteuid(0)) {} unlink(fn); /* security: mst:18-Apr-00 */ f=fopen(fn, "w"); (void)reseteuid(); if (f) { log_file_module(f); fclose(f); } else errorp(0, "handle_sigusr2", "cannot open %s for writing", fn); signal(SIGUSR2, sig_usr2); } static void set_sig(void) { signal(SIGTERM, sig_quit); signal(SIGQUIT, sig_quit); signal(SIGINT, sig_quit); signal(SIGPIPE, sig_pipe); signal(SIGHUP, sig_hup); signal(SIGUSR1, sig_usr1); signal(SIGUSR2, sig_usr2); if (use_mmap) signal(SIGBUS, sig_bus_mmap); /* in nwfile.c */ } #include int main(int argc, char **argv) { #if !CALL_NWCONN_OVER_SOCKET int shm_id; #endif time_t last_time=time(NULL); #if CALL_NWCONN_OVER_SOCKET if (argc != 4 || 3!=sscanf(argv[3], "()INIT-:%x,%x,%x-", &father_pid, &sock_nwbind, &sock_echo)) { fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock-\n"); exit(1); } #else if (argc != 4 || 4!=sscanf(argv[3], "()INIT-:%x,%x,%x,%x-", &father_pid, &sock_nwbind, &sock_echo, &shm_id)) { fprintf(stderr, "usage nwconn connid FROM_ADDR ()INIT-:pid,nwbindsock,echosock,shm_id-\n"); exit(1); } #endif prog_title=argv[3]; if (setuid(0)) {} if (setgid(0)) {} act_connection = atoi(*(argv+1)); #if !CALL_NWCONN_OVER_SOCKET nwconn_state = shmat(shm_id, NULL, SHM_W); if (nwconn_state == (char *)-1) { errorp(0, "Can't attach shared memory segment", NULL); exit(1); } #endif init_tools(NWCONN, 0); memset(saved_readbuff, 0, sizeof(saved_readbuff)); XDPRINTF((3, 0, "FATHER PID=%d, ADDR=%s CON:%d", father_pid, *(argv+2), act_connection)); adr_to_ipx_addr(&from_addr, *(argv+2)); if (nw_init_connect()) exit(1); act_pid = getpid(); #ifdef LINUX set_emu_tli(); #endif last_sequence = -9999; if (get_ipx_addr(&my_addr)) exit(1); #if CALL_NWCONN_OVER_SOCKET # if 1 # ifdef SIOCIPXNCPCONN { int conn = act_connection; int result = ioctl(0, SIOCIPXNCPCONN, &conn); XDPRINTF((3, 0, "ioctl:SIOCIPXNCPCONN result=%d", result)); } # endif # endif #endif set_default_guid(); nwconn_set_program_title(NULL); ud.opt.len = sizeof(uint8); ud.opt.maxlen = sizeof(uint8); ud.opt.buf = (char*)&ipx_pack_typ; ud.addr.len = sizeof(ipxAddr_t); ud.addr.maxlen = sizeof(ipxAddr_t); ud.addr.buf = (char*)&from_addr; ud.udata.buf = (char*)&ipxdata; U16_TO_BE16(0x3333, ncpresponse->type); ncpresponse->task = (uint8) 1; /* allways 1 */ ncpresponse->connection = (uint8)act_connection; ncpresponse->high_connection = (uint8)(act_connection >> 8); set_sig(); while ( !(fl_get_int&1) ) { int data_len; /* We should reply 'Request Being Processed' if request arrived twice * or more and nwconn actually busy, if nwconn is free, we are simply * resend previous reply. * We are set the flag in shared memory indicating what nwconn is busy * and check it later in ncpserv. /lenz */ #if !CALL_NWCONN_OVER_SOCKET nwconn_state[act_connection] = 0; /* nwconn is free */ #endif data_len = read(0, readbuff, sizeof(readbuff)); #if !CALL_NWCONN_OVER_SOCKET nwconn_state[act_connection] = 1; /* nwconn is busy */ #endif /* this read is a pipe or a socket read, * depending on CALL_NWCONN_OVER_SOCKET */ if (fl_get_int) { if (fl_get_int & 1) break; if (fl_get_int & 2) get_new_debug(); if (fl_get_int & 4) handle_extern_command(); if (fl_get_int & 8) handle_sigusr2(); } if (data_len > 0) { XDPRINTF((99, 0, "NWCONN GOT DATA len = %d",data_len)); ncpresponse->connect_status = (uint8) 0; ncpresponse->task = ncprequest->task; if ((ncp_type = (int)GET_BE16(ncprequest->type)) == 0x3333) { /* this is a response packet */ data_len -= sizeof(NCPRESPONSE); if (saved_sequence > -1 && ((int)(ncprequest->sequence) == saved_sequence) && !ncprequest->function) { /* comes from nwbind */ handle_after_bind(); } else { /* OK for direct sending */ XDPRINTF((6,0, "NWCONN:direct sending:type 0x3333, completition=0x%x, len=%d", (int)(ncprequest->function), data_len)); if (data_len) memcpy(responsedata, readbuff+sizeof(NCPRESPONSE), data_len); ncpresponse->connect_status = ((NCPRESPONSE*)readbuff)->connect_status; ncp_response(ncprequest->sequence, ncprequest->task, ncprequest->function, data_len); } saved_sequence = -1; } else { /* this calls I must handle, it is a request */ act_time=time(NULL); act_ncpsequence=(int)(ncprequest->sequence); if (act_time > last_time+60 && saved_sequence == -1) { /* ca. 0.5 min. reset wdogs, 5 min as in original is too long for me. /lenz*/ call_nwbind(1); last_time=act_time; } #if ENABLE_BURSTMODE if (ncp_type == 0x7777) { /* BURST-MODE */ XDPRINTF((2, 0, "Packet Burst data packet received: len=%d", data_len)); handle_burst((BURSTPACKET*)readbuff, data_len); } else #endif if (ncp_type == 0x2121) { /* request from nwbind */ /* mst:25-Apr-00 */ requestlen = data_len - sizeof(NCPREQUEST); handle_nwbind_request(); } else { int result; requestlen = data_len - sizeof(NCPREQUEST); if (0 != (result = handle_ncp_serv()) ) { if (result == -2) { /* here the actual call must be saved * because we need it later, when the request to nwbind * returns. */ memcpy(saved_readbuff, readbuff, data_len); saved_sequence = (int)(ncprequest->sequence); } else saved_sequence = -1; /* this call must go to nwbind */ call_nwbind(0); } } } } } /* while */ if (seteuid(0)) {} # ifdef SIOCIPXNCPCONN { int conn = -act_connection; (void)ioctl(0, SIOCIPXNCPCONN, &conn); } # endif close_all(); XDPRINTF((3,0, "leave nwconn pid=%d", getpid())); return(0); }