From 4c28bdd3cd4405ed41a59b540aab06a67d87e2b7 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Thu, 28 May 2026 21:04:26 +0200 Subject: [PATCH] server: recycle stale temporary directory handles 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. --- src/connect.c | 57 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/connect.c b/src/connect.c index 07d2811..d380e80 100644 --- a/src/connect.c +++ b/src/connect.c @@ -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,