server: recycle stale temporary directory handles
All checks were successful
Source release / source-package (push) Successful in 47s

Novell NCOPY can allocate temporary directory handles while checking
effective directory rights before copying.  On long-running test runs the
temporary directory handle table may already contain stale entries, causing
Allocate Temporary Directory Handle to fail with -0x9d / "no directory
handles".  NCOPY then falls back to relative GetEffectiveRights requests that
fail and reports:

    NWGetEffectiveDirectoryRights returned an error.

Restore recycling of stale temporary directory handles in xinsert_new_dir().
Free slots are still preferred.  If none are available and the caller requests
a temporary handle, recycle the oldest temporary handle for the same task first,
then the oldest temporary handle globally.  Permanent directory handles are not
recycled.

This matches the expected NetWare-style behaviour for temporary handles and
allows NCOPY-style allocate-temp-handle / get-effective-rights flows to keep
working after repeated tests.
This commit is contained in:
Mario Fetka
2026-05-28 21:04:26 +02:00
parent ca32f0a128
commit 4c28bdd3cd

View File

@@ -2485,26 +2485,55 @@ int xinsert_new_dir(int volume, uint8 *path, int dev, int inode, int drive, int
{
int j = 0;
int freehandle = 0;
#if 0
time_t lowtime = time(NULL);
int timedhandle = 0;
#endif
/* first look, whether drive is allready in use */
int timedhandle_same_task = 0;
int timedhandle_any_task = 0;
time_t lowtime_same_task = time(NULL);
time_t lowtime_any_task = time(NULL);
/*
* Temporary directory handles are supposed to be freed automatically at
* End Of Job. Some DOS requesters/tools, notably Novell NCOPY through
* Client32, can allocate temp handles without sending EOJ often enough for
* MARS-NWE's small dir-handle table. The old code only reused empty slots
* and returned 0x9d (no directory handles) once the table had filled with
* stale temp handles. That makes NCOPY fail before the actual copy with
* "NWGetEffectiveDirectoryRights returned an error."
*
* Prefer a genuinely free slot. If the table is full and this is another
* temp-handle allocation, recycle the oldest temp handle from the same task;
* only if none exists, recycle the oldest temp handle from any task.
* Permanent handles are never recycled here.
*/
for (j = (used_dirs) ? 1 : 0; j < (int)used_dirs; j++) {
NW_DIR *d = &(dirs[j]);
if (!d->inode)
if (!d->inode) {
freehandle = j+1;
#if 0
} else if (d->is_temp && d->timestamp < lowtime) {
timedhandle = j+1;
lowtime = d->timestamp;
} else if (is_temp && d->is_temp) {
if (d->task == (uint8)task && d->timestamp <= lowtime_same_task) {
timedhandle_same_task = j+1;
lowtime_same_task = d->timestamp;
}
if (d->timestamp <= lowtime_any_task) {
timedhandle_any_task = j+1;
lowtime_any_task = d->timestamp;
}
}
#endif
}
if (!freehandle && used_dirs < MAX_NW_DIRS) freehandle = ++used_dirs;
#if 0
if (!freehandle) freehandle = timedhandle;
#endif
if (!freehandle && is_temp) {
freehandle = timedhandle_same_task ? timedhandle_same_task : timedhandle_any_task;
if (freehandle) {
NW_DIR *d = &(dirs[freehandle-1]);
XDPRINTF((3,0,"Recycle stale temp dhandle:%d old='%d:%s' oldtask=%d newtask=%d",
freehandle, d->volume, d->path ? (char*)d->path : "",
d->task, task));
if (d->path) {
xfree(d->path);
d->path = NULL;
}
d->inode = 0;
}
}
if (freehandle){
(void)change_dir_entry(&(dirs[freehandle-1]),
volume, path, dev, inode,