diff --git a/TODO.md b/TODO.md index 1a28b78..94020c6 100644 --- a/TODO.md +++ b/TODO.md @@ -63,11 +63,8 @@ Current status: Follow-up: -- Implement or deliberately reject the following endpoints after client evidence - or direct protocol tests: - - `NCP 0x16/0x18 Restore Directory Handle`: verify the exact SDK - request/reply layout, then implement the documented function 22 / - subfunction 24 directory-handle semantics. +- Implement or deliberately reject remaining endpoint gaps after client + evidence or direct protocol tests. - Keep SDK details close to the corresponding endpoint in `nwconn.c`, and keep broader prioritization/status here in `TODO.md`. diff --git a/include/connect.h b/include/connect.h index 6728e2b..2870cf6 100644 --- a/include/connect.h +++ b/include/connect.h @@ -179,6 +179,9 @@ extern int nw_set_dir_handle(int targetdir, int dir_handle, extern int nw_get_directory_path(int dir_handle, uint8 *name, int size_name); +extern int nw_extract_base_handle(int dir_handle, uint8 *handle_id); +extern int nw_restore_base_handle(uint8 *handle_id, int task, int *eff_rights); + extern int nw_get_vol_number(int dir_handle); diff --git a/src/connect.c b/src/connect.c index d380e80..70bc1fc 100644 --- a/src/connect.c +++ b/src/connect.c @@ -85,6 +85,33 @@ typedef struct { NW_DIR dirs[MAX_NW_DIRS]; int used_dirs=0; + +#define MAX_EXTRACTED_BASE_HANDLES 64 + +typedef struct { + uint32 id; + int volume; + uint8 *path; + dev_t dev; + ino_t inode; + uint8 task; +} EXTRACTED_BASE_HANDLE; + +static EXTRACTED_BASE_HANDLE extracted_base_handles[MAX_EXTRACTED_BASE_HANDLES]; +static uint32 next_extracted_base_handle = 1; + +static void clear_extracted_base_handles(int task) +{ + int j; + for (j = 0; j < MAX_EXTRACTED_BASE_HANDLES; j++) { + EXTRACTED_BASE_HANDLE *ebh = &(extracted_base_handles[j]); + if (ebh->id && (task < 0 || ebh->task == (uint8)task)) { + xfree(ebh->path); + memset(ebh, 0, sizeof(*ebh)); + } + } + if (task < 0) next_extracted_base_handle = 1; +} int act_uid=-1; /* unix uid 0=root */ int act_gid=-1; /* unix gid */ int act_obj_id=0L; /* mars_nwe UID, 0=not logged in, 1=supervisor */ @@ -2370,6 +2397,7 @@ int nw_init_connect(void) } init_file_module(-1); + clear_extracted_base_handles(-1); if (connect_is_init) { k = 0; @@ -2458,6 +2486,7 @@ int nw_free_handles(int task) else { NW_DIR *d = &(dirs[0]); int k = used_dirs; + clear_extracted_base_handles(task); while (k--) { if (d->is_temp && d->task == task) { /* only actual task */ @@ -2834,6 +2863,80 @@ int nw_get_directory_path(int dir_handle, uint8 *name, int size_name) return(result); } + +int nw_extract_base_handle(int dir_handle, uint8 *handle_id) +/* Extract a Base Handle: stores an opaque handle id for Restore Directory Handle */ +{ + int result = -0x9b; + if (dir_handle > 0 && --dir_handle < (int)used_dirs) { + NW_DIR *d = &(dirs[dir_handle]); + if (d->inode && !d->is_temp && d->volume < used_nw_volumes) { + int j; + EXTRACTED_BASE_HANDLE *free_ebh = NULL; + for (j = 0; j < MAX_EXTRACTED_BASE_HANDLES; j++) { + EXTRACTED_BASE_HANDLE *ebh = &(extracted_base_handles[j]); + if (!ebh->id && !free_ebh) free_ebh = ebh; + if (ebh->id && ebh->volume == d->volume && ebh->path + && !strcmp((char*)ebh->path, (char*)d->path)) { + free_ebh = ebh; + break; + } + } + if (free_ebh) { + if (!free_ebh->id) { + free_ebh->id = next_extracted_base_handle++; + if (!next_extracted_base_handle) next_extracted_base_handle = 1; + } + xfree(free_ebh->path); + free_ebh->path = xmalloc(strlen((char*)d->path)+1); + strcpy((char*)free_ebh->path, (char*)d->path); + free_ebh->volume = d->volume; + free_ebh->dev = d->dev; + free_ebh->inode = d->inode; + free_ebh->task = d->task; + U32_TO_BE32(free_ebh->id, handle_id); + result = 0; + } else result = -0x96; /* server out of memory/table space */ + } + } + XDPRINTF((4,0,"Extract base handle from dhandle=%d result=0x%x", dir_handle+1, result)); + return(result); +} + +int nw_restore_base_handle(uint8 *handle_id, int task, int *eff_rights) +/* Restore an Extracted Base Handle: allocate a new permanent handle from a saved id */ +{ + uint32 id = GET_BE32(handle_id); + int j; + int result = -0x9b; + for (j = 0; j < MAX_EXTRACTED_BASE_HANDLES; j++) { + EXTRACTED_BASE_HANDLE *ebh = &(extracted_base_handles[j]); + if (ebh->id == id && ebh->path && ebh->volume >= 0 && ebh->volume < used_nw_volumes) { + NW_PATH nwpath; + struct stat stbuff; + uint8 unixname[257]; + nwpath.volume = ebh->volume; + nwpath.fn[0] = '\0'; + strmaxcpy(nwpath.path, (char*)ebh->path, sizeof(nwpath.path)-1); + xstrcpy(unixname, build_unix_name(&nwpath, 0)); + if (stat((char*)unixname, &stbuff)) { + result = -0x9c; /* invalid path */ + } else if (!S_ISDIR(stbuff.st_mode)) { + result = -0x9c; + } else { + result = xinsert_new_dir(nwpath.volume, nwpath.path, + stbuff.st_dev, stbuff.st_ino, 0, 0, task); + if (result > 0 && eff_rights) { + *eff_rights = tru_get_eff_rights(nwpath.volume, unixname, &stbuff); + } + } + break; + } + } + XDPRINTF((4,0,"Restore base handle id=0x%x task=%d result=0x%x", id, task, result)); + return(result); +} + int nw_get_vol_number(int dir_handle) /* Get Volume Nummmer with Handle */ { diff --git a/src/nwconn.c b/src/nwconn.c index 4f57901..f75633b 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -1015,29 +1015,73 @@ static int handle_ncp_serv(void) } break; -#if 0 - case 0x18 : { /* Restore Directory Handle */ + case 0x17 : { /* Extract a Base Handle */ /* - * TODO(ncp-compat): Implement NCP 0x16/0x18 - * Restore Directory Handle. + * NCP 0x2222/22/23 Extract a Base Handle. * - * SDK context: this is function 22 (0x16), subfunction 24 - * (0x18). It belongs to the old directory-handle family - * and should be implemented only after the exact SDK - * request/reply layout and handle-lifetime semantics are - * verified. It is related to the directory-handle table - * calls implemented nearby: - * 0x12 Alloc Permanent Directory Handle - * 0x13 Alloc Temporary Directory Handle - * 0x14 Deallocate Directory Handle - * 0x16 Alloc Special Temporary Directory 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. * - * Do not guess here: old DOS clients may depend on exact - * permanent-vs-temporary handle lifetime semantics. + * 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; -#endif case 0x19 : { /* Set Directory Information * Modifies basic directory information as creation date and