nwconn: implement extracted base handle restore
All checks were successful
Source release / source-package (push) Successful in 54s

Wire NCP 0x16/0x17 Extract a Base Handle and NCP 0x16/0x18 Restore an
Extracted Base Handle to connection-local directory-handle state.

The WebSDK documents NCP 0x2222/22/23 as taking a DirectoryHandle and
returning a 14-byte save buffer composed of a 10-byte ServerNetworkAddress
plus a 4-byte HandleID. The same documentation describes NCP 0x2222/22/24 as
taking that saved ServerNetworkAddress/HandleID pair and returning a
NewDirectoryHandle plus AccessRightsMask.

The SDK headers expose these calls as NWSaveDirectoryHandle() and
NWRestoreDirectoryHandle(), with the save buffer explicitly documented as 14
bytes. The Rust nwserver and lwared references do not implement this older
save/restore pair, and newer clients typically use the normal allocate/set
directory-handle calls instead, so keep the MARS-NWE HandleID opaque and
connection-local rather than guessing a global NetWare directory-base number.

Store extracted base-handle IDs in a small per-connection table that records
the saved volume/path tuple. Extract requires a live permanent directory
handle, and Restore validates the saved server address against this server
before allocating a new permanent directory handle for the saved path.

Add the SDK request/reply semantics to the inline endpoint comments and remove
the corresponding TODO entry.

This enables the documented endpoint path while keeping the saved HandleID
conservative and private to MARS-NWE.
This commit is contained in:
Mario Fetka
2026-05-29 22:06:58 +00:00
parent 701e33e0ce
commit c80861b92b
4 changed files with 169 additions and 22 deletions

View File

@@ -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 */
{