diff --git a/files/directory.c b/files/directory.c index 553f627f799..0c7b94f3475 100644 --- a/files/directory.c +++ b/files/directory.c @@ -582,8 +582,7 @@ DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext, p = strrchr( name, '.' ); if (p && !strchr( p, '/' ) && !strchr( p, '\\' )) ext = NULL; /* Ignore the specified extension */ - if ((*name && (name[1] == ':')) || - strchr( name, '/' ) || strchr( name, '\\' )) + if (FILE_contains_path (name)) path = NULL; /* Ignore path if name already contains a path */ if (path && !*path) path = NULL; /* Ignore empty path */ @@ -604,7 +603,7 @@ DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext, /* If the name contains an explicit path, everything's easy */ - if ((*name && (name[1] == ':')) || strchr( name, '/' ) || strchr( name, '\\' )) + if (FILE_contains_path(name)) { ret = DOSFS_GetFullName( name, TRUE, full_name ); goto done; @@ -746,3 +745,104 @@ DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, } +/*********************************************************************** + * search_alternate_path + * + * + * FIXME: should return long path names.? + */ +static BOOL search_alternate_path(LPCSTR dll_path, LPCSTR name, LPCSTR ext, + DOS_FULL_NAME *full_name) +{ + LPCSTR p; + LPSTR tmp = NULL; + BOOL ret = TRUE; + + /* First check the supplied parameters */ + + p = strrchr( name, '.' ); + if (p && !strchr( p, '/' ) && !strchr( p, '\\' )) + ext = NULL; /* Ignore the specified extension */ + + /* Allocate a buffer for the file name and extension */ + + if (ext) + { + DWORD len = strlen(name) + strlen(ext); + if (!(tmp = HeapAlloc( GetProcessHeap(), 0, len + 1 ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + strcpy( tmp, name ); + strcat( tmp, ext ); + name = tmp; + } + + if (DIR_TryEnvironmentPath (name, full_name, dll_path)) + ; + else if (DOSFS_GetFullName (name, TRUE, full_name)) /* current dir */ + ; + else if (DIR_TryPath (&DIR_System, name, full_name)) /* System dir */ + ; + else if (DIR_TryPath (&DIR_Windows, name, full_name)) /* Windows dir */ + ; + else + ret = DIR_TryEnvironmentPath( name, full_name, NULL ); + + if (tmp) HeapFree( GetProcessHeap(), 0, tmp ); + return ret; +} + + +/*********************************************************************** + * DIR_SearchAlternatePath + * + * Searches for a specified file in the search path. + * + * PARAMS + * dll_path [I] Path to search + * name [I] Filename to search for. + * ext [I] File extension to append to file name. The first + * character must be a period. This parameter is + * specified only if the filename given does not + * contain an extension. + * buflen [I] size of buffer, in characters + * buffer [O] buffer for found filename + * lastpart [O] address of pointer to last used character in + * buffer (the final '\') (May be NULL) + * + * RETURNS + * Success: length of string copied into buffer, not including + * terminating null character. If the filename found is + * longer than the length of the buffer, the length of the + * filename is returned. + * Failure: Zero + * + * NOTES + * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND) + */ +DWORD DIR_SearchAlternatePath( LPCSTR dll_path, LPCSTR name, LPCSTR ext, + DWORD buflen, LPSTR buffer, LPSTR *lastpart ) +{ + LPSTR p, res; + DOS_FULL_NAME full_name; + + if (!search_alternate_path( dll_path, name, ext, &full_name)) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return 0; + } + lstrcpynA( buffer, full_name.short_name, buflen ); + res = full_name.long_name + + strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' )); + while (*res == '/') res++; + if (buflen) + { + if (buflen > 3) lstrcpynA( buffer + 3, res, buflen - 3 ); + for (p = buffer; *p; p++) if (*p == '/') *p = '\\'; + if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1; + } + TRACE("Returning %d\n", strlen(res) + 3 ); + return strlen(res) + 3; +} diff --git a/include/file.h b/include/file.h index b661faf3bc7..ba67781b96c 100644 --- a/include/file.h +++ b/include/file.h @@ -61,6 +61,12 @@ inline static char FILE_toupper( char c ) return c; } +inline static int FILE_contains_path (LPCSTR name) +{ + return ((*name && (name[1] == ':')) || + strchr (name, '/') || strchr (name, '\\')); +} + /* files/file.c */ extern int FILE_strcasecmp( const char *str1, const char *str2 ); extern int FILE_strncasecmp( const char *str1, const char *str2, int len ); @@ -80,6 +86,8 @@ extern LONG WINAPI WIN16_hread(HFILE16,SEGPTR,LONG); extern int DIR_Init(void); extern UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count ); extern UINT DIR_GetSystemUnixDir( LPSTR path, UINT count ); +extern DWORD DIR_SearchAlternatePath( LPCSTR dll_path, LPCSTR name, LPCSTR ext, + DWORD buflen, LPSTR buffer, LPSTR *lastpart); extern DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext, DOS_FULL_NAME *full_name, BOOL win32 ); diff --git a/include/module.h b/include/module.h index 27f6efd68ef..b98bcd5824b 100644 --- a/include/module.h +++ b/include/module.h @@ -234,6 +234,7 @@ extern WINE_MODREF *PE_CreateModule( HMODULE hModule, LPCSTR filename, DWORD flags, HANDLE hFile, BOOL builtin ); extern void PE_InitTls(void); extern BOOL PE_InitDLL( HMODULE module, DWORD type, LPVOID lpReserved ); +extern DWORD PE_fixup_imports(WINE_MODREF *wm); /* loader/loadorder.c */ extern void MODULE_InitLoadOrder(void); diff --git a/loader/module.c b/loader/module.c index 692ea11352b..5522423949a 100644 --- a/loader/module.c +++ b/loader/module.c @@ -1315,6 +1315,37 @@ HMODULE WINAPI LoadLibraryExA(LPCSTR libname, HANDLE hfile, DWORD flags) return wm ? wm->module : 0; } +/*********************************************************************** + * allocate_lib_dir + * + * helper for MODULE_LoadLibraryExA. Allocate space to hold the directory + * portion of the provided name and put the name in it. + * + */ +static LPCSTR allocate_lib_dir(LPCSTR libname) +{ + LPCSTR p, pmax; + LPSTR result; + int length; + + pmax = libname; + if ((p = strrchr( pmax, '\\' ))) pmax = p + 1; + if ((p = strrchr( pmax, '/' ))) pmax = p + 1; /* Naughty. MSDN says don't */ + if (pmax == libname && pmax[0] && pmax[1] == ':') pmax += 2; + + length = pmax - libname; + + result = HeapAlloc (GetProcessHeap(), 0, length+1); + + if (result) + { + strncpy (result, libname, length); + result [length] = '\0'; + } + + return result; +} + /*********************************************************************** * MODULE_LoadLibraryExA (internal) * @@ -1325,6 +1356,16 @@ HMODULE WINAPI LoadLibraryExA(LPCSTR libname, HANDLE hfile, DWORD flags) * ignore the parameter because it would be extremely difficult to * integrate this with different types of module represenations. * + * libdir is used to support LOAD_WITH_ALTERED_SEARCH_PATH during the recursion + * on this function. When first called from LoadLibraryExA it will be + * NULL but thereafter it may point to a buffer containing the path + * portion of the library name. Note that the recursion all occurs + * within a Critical section (see LoadLibraryExA) so the use of a + * static is acceptable. + * (We have to use a static variable at some point anyway, to pass the + * information from BUILTIN32_dlopen through dlopen and the builtin's + * init function into load_library). + * allocated_libdir is TRUE in the stack frame that allocated libdir */ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) { @@ -1334,14 +1375,30 @@ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) enum loadorder_type loadorder[LOADORDER_NTYPES]; LPSTR filename, p; const char *filetype = ""; + DWORD found; + BOOL allocated_libdir = FALSE; + static LPCSTR libdir = NULL; /* See above */ if ( !libname ) return NULL; filename = HeapAlloc ( GetProcessHeap(), 0, MAX_PATH + 1 ); if ( !filename ) return NULL; + RtlAcquirePebLock(); + + if ((flags & LOAD_WITH_ALTERED_SEARCH_PATH) && FILE_contains_path(libname)) + { + if (!(libdir = allocate_lib_dir(libname))) goto error; + allocated_libdir = TRUE; + } + + if (!libdir || allocated_libdir) + found = SearchPathA(NULL, libname, ".dll", MAX_PATH, filename, NULL); + else + found = DIR_SearchAlternatePath(libdir, libname, ".dll", MAX_PATH, filename, NULL); + /* build the modules filename */ - if (!SearchPathA( NULL, libname, ".dll", MAX_PATH, filename, NULL )) + if (!found) { if ( ! GetSystemDirectoryA ( filename, MAX_PATH ) ) goto error; @@ -1357,26 +1414,18 @@ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) strcpy ( filename, libname ); else { - if ( strchr ( libname, '\\' ) || strchr ( libname, ':') || strchr ( libname, '/' ) ) - goto error; - else - { - strcat ( filename, "\\" ); - strcat ( filename, libname ); - } + if (FILE_contains_path(libname)) goto error; + strcat ( filename, "\\" ); + strcat ( filename, libname ); } /* if the filename doesn't have an extension append .DLL */ if (!(p = strrchr( filename, '.')) || strchr( p, '/' ) || strchr( p, '\\')) - strcat( filename, ".DLL" ); + strcat( filename, ".dll" ); } - RtlAcquirePebLock(); - /* Check for already loaded module */ - if (!(pwm = MODULE_FindModule(filename)) && - /* no path in libpath */ - !strchr( libname, '\\' ) && !strchr( libname, ':') && !strchr( libname, '/' )) + if (!(pwm = MODULE_FindModule(filename)) && !FILE_contains_path(libname)) { LPSTR fn = HeapAlloc ( GetProcessHeap(), 0, MAX_PATH + 1 ); if (fn) @@ -1404,11 +1453,15 @@ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) if ((pwm->flags & WINE_MODREF_DONT_RESOLVE_REFS) && !(flags & DONT_RESOLVE_DLL_REFERENCES)) { - extern DWORD fixup_imports(WINE_MODREF *wm); /*FIXME*/ pwm->flags &= ~WINE_MODREF_DONT_RESOLVE_REFS; - fixup_imports( pwm ); + PE_fixup_imports( pwm ); } TRACE("Already loaded module '%s' at 0x%08x, count=%d, \n", filename, pwm->module, pwm->refCount); + if (allocated_libdir) + { + HeapFree ( GetProcessHeap(), 0, (LPSTR)libdir ); + libdir = NULL; + } RtlReleasePebLock(); HeapFree ( GetProcessHeap(), 0, filename ); return pwm; @@ -1456,6 +1509,11 @@ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) /* decrement the dependencies through the MODULE_FreeLibrary call. */ pwm->refCount++; + if (allocated_libdir) + { + HeapFree ( GetProcessHeap(), 0, (LPSTR)libdir ); + libdir = NULL; + } RtlReleasePebLock(); SetLastError( err ); /* restore last error */ HeapFree ( GetProcessHeap(), 0, filename ); @@ -1466,8 +1524,13 @@ WINE_MODREF *MODULE_LoadLibraryExA( LPCSTR libname, HFILE hfile, DWORD flags ) break; } - RtlReleasePebLock(); error: + if (allocated_libdir) + { + HeapFree ( GetProcessHeap(), 0, (LPSTR)libdir ); + libdir = NULL; + } + RtlReleasePebLock(); WARN("Failed to load module '%s'; error=0x%08lx, \n", filename, GetLastError()); HeapFree ( GetProcessHeap(), 0, filename ); return NULL; diff --git a/loader/pe_image.c b/loader/pe_image.c index bc9f52689f0..31884a6f4d4 100644 --- a/loader/pe_image.c +++ b/loader/pe_image.c @@ -17,7 +17,7 @@ * - If you want to enhance, speed up or clean up something in here, think * twice WHY it is implemented in that strange way. There is usually a reason. * Though sometimes it might just be lazyness ;) - * - In PE_MapImage, right before fixup_imports() all external and internal + * - In PE_MapImage, right before PE_fixup_imports() all external and internal * state MUST be correct since this function can be called with the SAME image * AGAIN. (Thats recursion for you.) That means MODREF.module and * NE_MODULE.module32. @@ -242,7 +242,10 @@ static FARPROC PE_FindExportedFunction( } } -DWORD fixup_imports( WINE_MODREF *wm ) +/**************************************************************** + * PE_fixup_imports + */ +DWORD PE_fixup_imports( WINE_MODREF *wm ) { IMAGE_IMPORT_DESCRIPTOR *pe_imp; unsigned int load_addr = wm->module; @@ -633,7 +636,8 @@ WINE_MODREF *PE_CreateModule( HMODULE hModule, LPCSTR filename, DWORD flags, /* Fixup Imports */ - if (!(wm->flags & WINE_MODREF_DONT_RESOLVE_REFS) && fixup_imports( wm )) + if (!(wm->flags & WINE_MODREF_DONT_RESOLVE_REFS) && + PE_fixup_imports( wm )) { /* remove entry from modref chain */