Skip to content

Commit 6a4c862

Browse files
JanWielemakerclaude
andcommitted
FIXED: Windows: UNC support for relative file names and shared object loading.
When the process working directory is a UNC path (e.g. set via a Windows shortcut's "Start in" field), resolving a relative file name via absolute_file_name/3, access_file/2 and friends failed, and open_shared_object/2, use_foreign_library/1 could not load DLLs identified by a UNC path. _xos_os_filenameW() used to unconditionally prepend "\\?\" and let GetFullPathNameW() append its result. When the CWD is UNC, that produced a malformed "\\?\\\host\share\..." path that NT rejects. Now run GetFullPathNameW() first, inspect the result, and use the "\\?\UNC\" prefix when it resolves to a UNC path. LoadLibraryExW() and AddDllDirectoryW() do not accept extended-length "\\?\" or "\\?\UNC\" prefixes and return ERROR_INVALID_PARAMETER, especially in combination with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR. Strip the prefix (converting "\\?\UNC\host\..." back to "\\host\...") before calling them. This also fixes a latent bug in win_add_dll_directory/2 where _xos_win_prefix_length() returned 4 for "\\?\UNC\..." (WIN_PATH_PREFIX matches first), leaving "UNC\host\..." as the directory passed to AddDllDirectoryW(). Discussion: https://swi-prolog.discourse.group/t/issues-with-unc-path/9010 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 355e941 commit 6a4c862

2 files changed

Lines changed: 55 additions & 11 deletions

File tree

src/os/windows/uxnt.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,13 +387,34 @@ _xos_os_filenameW(const char *cname, wchar_t *osname, size_t len)
387387
return osname;
388388
}
389389
} else
390-
{ wcscpy(s, WIN_PATH_PREFIX);
391-
s += wcslen(s);
392-
len -= (s-osname);
393-
rc = GetFullPathNameW(buf, len, s, NULL);
394-
if ( rc <= len )
395-
{ remove_win_prefix(s);
396-
return osname;
390+
{ TCHAR full[PATH_MAX];
391+
392+
rc = GetFullPathNameW(buf, PATH_MAX, full, NULL);
393+
if ( rc > 0 && rc < PATH_MAX )
394+
{ remove_win_prefix(full);
395+
/* GetFullPathNameW may resolve a relative name to a UNC path when
396+
the process CWD is a UNC path. In that case we must use the
397+
\\?\UNC\ prefix rather than \\?\, or NT rejects the path.
398+
*/
399+
if ( full[0] == '\\' && full[1] == '\\' )
400+
{ size_t plen = wcslen(WIN_UNC_PREFIX);
401+
size_t tlen = wcslen(full+1); /* skip one leading '\' */
402+
403+
if ( plen + tlen + 1 <= len )
404+
{ wcscpy(s, WIN_UNC_PREFIX);
405+
wcscpy(s+plen, full+1);
406+
return osname;
407+
}
408+
} else
409+
{ size_t plen = wcslen(WIN_PATH_PREFIX);
410+
size_t tlen = wcslen(full);
411+
412+
if ( plen + tlen + 1 <= len )
413+
{ wcscpy(s, WIN_PATH_PREFIX);
414+
wcscpy(s+plen, full);
415+
return osname;
416+
}
417+
}
397418
}
398419
}
399420

src/pl-nt.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,24 @@ load_library_search_flags(void)
718718
}
719719

720720

721+
/* LoadLibraryExW() and AddDllDirectoryW() do not accept extended-length
722+
* "\\?\" prefixes. Strip a "\\?\" prefix in place; convert "\\?\UNC\"
723+
* into its plain UNC form "\\". Returns a pointer into buf.
724+
*/
725+
726+
static wchar_t *
727+
strip_win_prefix(wchar_t *buf)
728+
{ if ( wcsncmp(buf, L"\\\\?\\UNC\\", 8) == 0 )
729+
{ buf[6] = L'\\'; /* overwrite 'C' of "UNC" */
730+
return buf + 6; /* now begins with "\\host\..." */
731+
}
732+
if ( wcsncmp(buf, L"\\\\?\\", 4) == 0 )
733+
return buf + 4;
734+
735+
return buf;
736+
}
737+
738+
721739
static
722740
PRED_IMPL("win_add_dll_directory", 2, win_add_dll_directory, 0)
723741
{ PRED_LD
@@ -733,7 +751,7 @@ PRED_IMPL("win_add_dll_directory", 2, win_add_dll_directory, 0)
733751
{ int eno;
734752

735753
/* AddDllDirectoryW() cannot handle "\\?\" */
736-
if ( (cookie = (*f_AddDllDirectoryW)(dirw + _xos_win_prefix_length(dirw))) )
754+
if ( (cookie = (*f_AddDllDirectoryW)(strip_win_prefix(dirw))) )
737755
{ DEBUG(MSG_WIN_API,
738756
SdprintfX("AddDllDirectory(%Ws) ok\n", dirw));
739757

@@ -786,12 +804,17 @@ PL_dlopen(const char *file, int flags) /* file is in UTF-8, POSIX path */
786804
{ HINSTANCE h;
787805
DWORD llflags = 0;
788806
wchar_t wfile[PATH_MAX];
807+
wchar_t *load_path = wfile;
789808

790809
if ( strchr(file, '/') || strchr(file, '\\' ) )
791810
{ if ( _xos_os_filenameW(file, wfile, PATH_MAX) == NULL )
792811
{ dlmsg = "Name too long";
793812
return NULL;
794813
}
814+
/* LoadLibraryExW() rejects "\\?\"-prefixed paths, especially in
815+
* combination with LOAD_LIBRARY_SEARCH_* flags.
816+
*/
817+
load_path = strip_win_prefix(wfile);
795818
} else
796819
{ wchar_t *w = wfile;
797820
wchar_t *e = &w[PATH_MAX-1];
@@ -809,12 +832,12 @@ PL_dlopen(const char *file, int flags) /* file is in UTF-8, POSIX path */
809832
*w = 0;
810833
}
811834

812-
DEBUG(MSG_WIN_API, SdprintfX("dlopen(%Ws)\n", wfile));
835+
DEBUG(MSG_WIN_API, SdprintfX("dlopen(%Ws)\n", load_path));
813836

814-
if ( is_windows_abs_path(wfile) )
837+
if ( is_windows_abs_path(load_path) )
815838
llflags |= load_library_search_flags();
816839

817-
if ( (h = LoadLibraryExW(wfile, NULL, llflags)) )
840+
if ( (h = LoadLibraryExW(load_path, NULL, llflags)) )
818841
{ dlmsg = "No Error";
819842
return (void *)h;
820843
}

0 commit comments

Comments
 (0)