pal::string_t coreclr_dll_path(libcoreclr_path);
append_path(&coreclr_dll_path, LIBCORECLR_NAME);
- if (!pal::load_library(coreclr_dll_path.c_str(), &g_coreclr))
+ if (!pal::load_library(&coreclr_dll_path, &g_coreclr))
{
return false;
}
if(WIN32)
- list(APPEND SOURCES ../../common/pal.windows.cpp)
+ list(APPEND SOURCES
+ ../../common/pal.windows.cpp
+ ../../common/longfile.windows.cpp)
else()
list(APPEND SOURCES ../../common/pal.unix.cpp)
endif()
../../../common/utils.cpp)
if(WIN32)
- list(APPEND SOURCES ../../../common/pal.windows.cpp)
+ list(APPEND SOURCES
+ ../../../common/pal.windows.cpp
+ ../../../common/longfile.windows.cpp)
else()
list(APPEND SOURCES ../../../common/pal.unix.cpp)
endif()
if(WIN32)
- list(APPEND SOURCES ../../common/pal.windows.cpp)
+ list(APPEND SOURCES
+ ../../common/pal.windows.cpp
+ ../../common/longfile.windows.cpp)
else()
list(APPEND SOURCES ../../common/pal.unix.cpp)
endif()
}
// Load library
- if (!pal::load_library(host_path.c_str(), h_host))
+ if (!pal::load_library(&host_path, h_host))
{
trace::info(_X("Load library of %s failed"), host_path.c_str());
return StatusCode::CoreHostLibLoadFailure;
--- /dev/null
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef _LONG_FILE_SUPPORT
+#define _LONG_FILE_SUPPORT
+
+class LongFile
+{
+public:
+ static const pal::string_t ExtendedPrefix;
+ static const pal::string_t DevicePathPrefix;
+ static const pal::string_t UNCPathPrefix;
+ static const pal::string_t UNCExtendedPathPrefix;
+ static const pal::char_t VolumeSeparatorChar;
+ static const pal::char_t DirectorySeparatorChar;
+ static const pal::char_t AltDirectorySeparatorChar;
+public:
+ static bool IsExtended(const pal::string_t& path);
+ static bool IsUNCExtended(const pal::string_t& path);
+ static bool ContainsDirectorySeparator(const pal::string_t & path);
+ static bool IsDirectorySeparator(const pal::char_t c);
+ static bool IsPathNotFullyQualified(const pal::string_t& path);
+ static bool IsDevice(const pal::string_t& path);
+ static bool ShouldNormalize(const pal::string_t& path);
+};
+#endif //_LONG_FILE_SUPPORT
--- /dev/null
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+//The logic in this file was ported from https://github.com/dotnet/coreclr/blob/54891e0650e69f08832f75a40dc102efc6115d38/src/utilcode/longfilepathwrappers.cpp
+//Please reflect any change here into the above file too!
+#include "pal.h"
+#include "trace.h"
+#include "utils.h"
+#include "longfile.h"
+
+const pal::char_t LongFile::DirectorySeparatorChar = _X('\\');
+const pal::char_t LongFile::AltDirectorySeparatorChar = _X('/');
+const pal::char_t LongFile::VolumeSeparatorChar = _X(':');
+const pal::string_t LongFile::ExtendedPrefix = _X("\\\\?\\");
+const pal::string_t LongFile::DevicePathPrefix = _X("\\\\.\\");
+const pal::string_t LongFile::UNCExtendedPathPrefix = _X("\\\\?\\UNC\\");
+const pal::string_t LongFile::UNCPathPrefix = _X("\\\\");
+
+bool ShouldNormalizeWorker(const pal::string_t& path)
+{
+ if (path.empty() || LongFile::IsDevice(path) || LongFile::IsExtended(path) || LongFile::IsUNCExtended(path))
+ return false;
+
+ if (!LongFile::IsPathNotFullyQualified(path) && path.size() < MAX_PATH)
+ return false;
+
+ return true;
+}
+
+//For longpath names on windows, if the paths are normalized they are always prefixed with
+//extended syntax, Windows does not do any more normalizations on this string and uses it as is
+//So we should ensure that there are NO adjacent DirectorySeparatorChar
+bool AssertRepeatingDirSeparator(const pal::string_t& path)
+{
+ if (path.empty())
+ return true;
+
+ pal::string_t path_to_check = path;
+ if (LongFile::IsDevice(path))
+ {
+ path_to_check.erase(0, LongFile::DevicePathPrefix.length());
+ }
+ else if (LongFile::IsExtended(path))
+ {
+ path_to_check.erase(0, LongFile::ExtendedPrefix.length());
+ }
+ else if (LongFile::IsUNCExtended(path))
+ {
+ path_to_check.erase(0, LongFile::UNCExtendedPathPrefix.length());
+ }
+ else if (path_to_check.compare(0, LongFile::UNCPathPrefix.length(), LongFile::UNCPathPrefix) == 0)
+ {
+ path_to_check.erase(0, LongFile::UNCPathPrefix.length());
+ }
+
+ pal::string_t dirSeparator;
+ dirSeparator.push_back(LongFile::DirectorySeparatorChar);
+ dirSeparator.push_back(LongFile::DirectorySeparatorChar);
+
+ assert(path_to_check.find(dirSeparator) == pal::string_t::npos);
+
+ pal::string_t altDirSeparator;
+ altDirSeparator.push_back(LongFile::AltDirectorySeparatorChar);
+ altDirSeparator.push_back(LongFile::AltDirectorySeparatorChar);
+
+ assert(path_to_check.find(altDirSeparator) == pal::string_t::npos);
+
+ return true;
+}
+bool LongFile::ShouldNormalize(const pal::string_t& path)
+{
+ bool retval = ShouldNormalizeWorker(path);
+ assert(retval || AssertRepeatingDirSeparator(path));
+ return retval;
+}
+
+bool LongFile::IsExtended(const pal::string_t& path)
+{
+ return path.compare(0, ExtendedPrefix.length(), ExtendedPrefix) == 0;
+}
+
+bool LongFile::IsUNCExtended(const pal::string_t& path)
+{
+ return path.compare(0, UNCExtendedPathPrefix.length(), UNCExtendedPathPrefix) == 0;
+}
+
+bool LongFile::IsDevice(const pal::string_t& path)
+{
+ return path.compare(0, DevicePathPrefix.length(), DevicePathPrefix) == 0;
+}
+
+// Relative here means it could be relative to current directory on the relevant drive
+// NOTE: Relative segments ( \..\) are not considered relative
+// Returns true if the path specified is relative to the current drive or working directory.
+// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+// validation of the path (URIs will be returned as relative as a result).
+// Handles paths that use the alternate directory separator. It is a frequent mistake to
+// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+
+bool LongFile::IsPathNotFullyQualified(const pal::string_t& path)
+{
+ if (path.length() < 2)
+ {
+ return true; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less).
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes
+ }
+
+ return !((path.length() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- "i.e. C:\"
+ && (path[1] == VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2]));
+}
+
+bool LongFile::ContainsDirectorySeparator(const pal::string_t & path)
+{
+ return path.find(DirectorySeparatorChar) != pal::string_t::npos ||
+ path.find(AltDirectorySeparatorChar) != pal::string_t::npos;
+}
+
+bool LongFile::IsDirectorySeparator(const pal::char_t c)
+{
+ return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
+}
int xtoi(const char_t* input);
- bool load_library(const char_t* path, dll_t* dll);
+ bool load_library(const string_t* path, dll_t* dll);
proc_t get_symbol(dll_t library, const char* name);
void unload_library(dll_t library);
}
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
- pal::char_t* buf = ::getcwd(nullptr, PATH_MAX + 1);
+ pal::char_t* buf = ::getcwd(nullptr, 0);
if (buf == nullptr)
{
if (errno == ENOENT)
return true;
}
-bool pal::load_library(const char_t* path, dll_t* dll)
+bool pal::load_library(const string_t* path, dll_t* dll)
{
- *dll = dlopen(path, RTLD_LAZY);
+ *dll = dlopen(path->c_str(), RTLD_LAZY);
if (*dll == nullptr)
{
trace::error(_X("Failed to load %s, error: %s"), path, dlerror());
mib[3] = -1;
char buf[PATH_MAX];
size_t cb = sizeof(buf);
- if (sysctl(mib, 4, buf, &cb, NULL, 0) == 0)
+ int error_code = 0;
+ error_code = sysctl(mib, 4, buf, &cb, NULL, 0);
+ if (error_code == 0)
{
recv->assign(buf);
return true;
}
+
// ENOMEM
+ if (error_code == ENOMEM)
+ {
+ size_t len = sysctl(mib, 4, NULL, NULL, NULL, 0);
+ std::unique_ptr<char[]> buffer = new (std::nothrow) char[len];
+
+ if (buffer == NULL)
+ {
+ return false;
+ }
+
+ error_code = sysctl(mib, 4, buffer, &len, NULL, 0);
+ if (error_code == 0)
+ {
+ recv->assign(buffer);
+ return true;
+ }
+ }
return false;
}
#else
#include "pal.h"
#include "trace.h"
#include "utils.h"
+#include "longfile.h"
#include <cassert>
#include <locale>
#include <codecvt>
#include <ShlObj.h>
+bool GetModuleFileNameWrapper(HMODULE hModule, pal::string_t* recv)
+{
+ pal::string_t path;
+ DWORD dwModuleFileName = MAX_PATH / 2;
+
+ do
+ {
+ path.resize(dwModuleFileName * 2);
+ dwModuleFileName = GetModuleFileNameW(hModule, (LPWSTR)path.data(), path.size());
+ } while (dwModuleFileName == path.size());
+
+ if (dwModuleFileName != 0)
+ {
+ *recv = path;
+ return true;
+ }
+
+ return false;
+
+}
+
pal::string_t pal::to_lower(const pal::string_t& in)
{
pal::string_t ret = in;
return false;
}
-bool pal::load_library(const char_t* path, dll_t* dll)
+bool pal::load_library(const string_t* in_path, dll_t* dll)
{
+ string_t path = *in_path;
+
// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR:
// In portable apps, coreclr would come from another directory than the host,
// so make sure coreclr dependencies can be resolved from coreclr.dll load dir.
- *dll = ::LoadLibraryExW(path, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+
+ if (LongFile::IsPathNotFullyQualified(path))
+ {
+ if (!pal::realpath(&path))
+ {
+ trace::error(_X("Failed to load the dll from [%s], HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
+ return false;
+ }
+ }
+
+ //Adding the assert to ensure relative paths which are not just filenames are not used for LoadLibrary Calls
+ assert(!LongFile::IsPathNotFullyQualified(path) || !LongFile::ContainsDirectorySeparator(path));
+
+ *dll = ::LoadLibraryExW(path.c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
if (*dll == nullptr)
{
trace::error(_X("Failed to load the dll from [%s], HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
// Pin the module
HMODULE dummy_module;
- if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, path, &dummy_module))
+ if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, path.c_str(), &dummy_module))
{
trace::error(_X("Failed to pin library [%s] in [%s]"), path, _STRINGIFY(__FUNCTION__));
return false;
if (trace::is_enabled())
{
- pal::char_t buf[PATH_MAX];
- ::GetModuleFileNameW(*dll, buf, PATH_MAX);
+ string_t buf;
+ GetModuleFileNameWrapper(*dll, &buf);
trace::info(_X("Loaded library from %s"), buf);
}
bool pal::get_own_executable_path(string_t* recv)
{
- char_t program_path[MAX_PATH];
- DWORD dwModuleFileName = ::GetModuleFileNameW(NULL, program_path, MAX_PATH);
- if (dwModuleFileName == 0 || dwModuleFileName >= MAX_PATH) {
- return false;
- }
- recv->assign(program_path);
- return true;
+ return GetModuleFileNameWrapper(NULL, recv);
}
static bool wchar_convert_helper(DWORD code_page, const char* cstr, int len, pal::string_t* out)
bool pal::realpath(string_t* path)
{
+
+ if (!LongFile::ShouldNormalize(*path))
+ {
+ return true;
+ }
+
char_t buf[MAX_PATH];
- auto res = ::GetFullPathNameW(path->c_str(), MAX_PATH, buf, nullptr);
- if (res == 0 || res > MAX_PATH)
+ auto size = ::GetFullPathNameW(path->c_str(), MAX_PATH, buf, nullptr);
+
+ if (size == 0)
{
trace::error(_X("Error resolving full path [%s]"), path->c_str());
return false;
}
- path->assign(buf);
+
+ if (size < MAX_PATH)
+ {
+ path->assign(buf);
+ return true;
+ }
+
+ string_t str;
+ str.resize(size + LongFile::UNCExtendedPathPrefix.length(), 0);
+
+ size = ::GetFullPathNameW(path->c_str() , size, (LPWSTR)str.data() , nullptr);
+ assert(size <= str.size());
+
+ if (size == 0)
+ {
+ trace::error(_X("Error resolving full path [%s]"), path->c_str());
+ return false;
+ }
+
+ const string_t* prefix = &LongFile::ExtendedPrefix;
+ //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
+ if (str.compare(0, LongFile::UNCPathPrefix.length(), LongFile::UNCPathPrefix) == 0)
+ {
+ prefix = &LongFile::UNCExtendedPathPrefix;
+ str.erase(0, LongFile::UNCPathPrefix.length());
+ size = size - LongFile::UNCPathPrefix.length();
+ }
+
+ str.insert(0, *prefix);
+ str.resize(size + prefix->length());
+ str.shrink_to_fit();
+ *path = str;
+
return true;
}
return false;
}
+ auto pathstring = path.c_str();
+ string_t normalized_path;
+ if (LongFile::ShouldNormalize(path))
+ {
+ normalized_path = path;
+ if (!pal::realpath(&normalized_path))
+ {
+ return false;
+ }
+ pathstring = normalized_path.c_str();
+ }
// We will attempt to fetch attributes for the file or folder in question that are
// returned only if they exist.
WIN32_FILE_ATTRIBUTE_DATA data;
- if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) != 0) {
+ if (GetFileAttributesExW(pathstring, GetFileExInfoStandard, &data) != 0) {
return true;
}
assert(list != nullptr);
std::vector<string_t>& files = *list;
+ string_t normalized_path(path);
- string_t search_string(path);
+ if (LongFile::ShouldNormalize(normalized_path))
+ {
+ if (!pal::realpath(&normalized_path))
+ {
+ return;
+ }
+ }
+
+ string_t search_string(normalized_path);
append_path(&search_string, pattern.c_str());
WIN32_FIND_DATAW data = { 0 };
+
auto handle = ::FindFirstFileExW(search_string.c_str(), FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
if (handle == INVALID_HANDLE_VALUE)
{
return StatusCode::CoreHostLibMissingFailure;
}
- if (!pal::load_library(fxr_path.c_str(), &fxr))
+ if (!pal::load_library(&fxr_path, &fxr))
{
trace::error(_X("The library %s was found, but loading it from %s failed"), LIBFXR_NAME, fxr_path.c_str());
trace::error(_X(" - Installing .NET Core prerequisites might help resolve this problem."));