Files
UnrealEngine/Engine/Source/Programs/UnrealBuildAccelerator/Detours/Private/Windows/UbaDetoursFunctionsUcrtBase.inl
2025-05-18 13:04:45 +08:00

428 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
wchar_t* Detoured__wgetcwd(wchar_t* buffer, int maxlen)
{
DETOURED_CALL(_wgetcwd);
//wchar_t* res = True__wgetcwd(buffer, maxlen);
//return res;
UBA_ASSERT(g_virtualWorkingDir.count < u32(maxlen));
memcpy(buffer, g_virtualWorkingDir.data, u64(g_virtualWorkingDir.count) * 2);
buffer[g_virtualWorkingDir.count - 1] = 0; // Remove last slash
#if UBA_DEBUG_VALIDATE
if (!g_runningRemote)
{
//wchar_t* res = True__wgetcwd(buffer, maxlen);
}
#endif
DEBUG_LOG_DETOURED(L"_wgetcwd", L"(%ls)", buffer);
return buffer;
}
wchar_t* Detoured__wfullpath(wchar_t* absPath, const wchar_t* relPath, size_t maxLength)
{
DETOURED_CALL(_wfullpath);
u64 relLen = 0;
if (relPath)
relLen = wcslen(relPath);
wchar_t* res = nullptr;
if (!absPath)
{
maxLength = relLen + g_virtualWorkingDir.count + 2;
if (g_isRunningWine)
res = (wchar_t*)malloc(maxLength * sizeof(WCHAR)); // Same as wine implementation
else
res = (wchar_t*)_malloc_base(maxLength * sizeof(WCHAR));
FixPath2(relPath, g_virtualWorkingDir.data, g_virtualWorkingDir.count, res, maxLength, nullptr);
UBA_ASSERT(res && wcslen(res) < maxLength - 1);
}
else if ((relPath && relPath[1] == ':' && relLen < maxLength) || (relLen + g_virtualWorkingDir.count <= maxLength))
{
FixPath2(relPath, g_virtualWorkingDir.data, g_virtualWorkingDir.count, absPath, maxLength, nullptr);
UBA_ASSERT(wcslen(absPath) < maxLength);
res = absPath;
}
//DEBUG_LOG_DETOURED(L"_wfullpath", L"(%ls) -> %ls", relPath, res); // This is very spammy
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess && !g_runningRemote)
{
wchar_t otherBuf[1024] = {};
SuppressDetourScope _;
wchar_t* test = True__wfullpath(otherBuf, relPath, maxLength); (void)test;
UBA_ASSERT((res != nullptr) == (test != nullptr) && (!res || (Equals(res, test, false) || wcschr(test, '~'))));
}
#endif
return res;
}
// Needed for ltcg builds on wine
char* Detoured__fullpath(char* absPath, const char* relPath, size_t maxLength)
{
DETOURED_CALL(_fullpath);
u64 relLen = 0;
if (relPath)
relLen = strlen(relPath);
StringBuffer<> wideRelPath;
wideRelPath.Appendf(L"%hs", relPath);
char* res = nullptr;
if (!absPath)
{
maxLength = relLen + g_virtualWorkingDir.count + 1;
if (g_isRunningWine)
res = (char*)malloc(maxLength); // Same as wine implementation
else
res = (char*)_malloc_base(maxLength);
if (!res)
return nullptr;
StringBuffer<> wres;
FixPath(wres, wideRelPath.data);
sprintf_s(res, maxLength, "%ls", wres.data);
UBA_ASSERT(res && wideRelPath.count < maxLength - 1);
}
else if ((relPath && relPath[1] == ':' && relLen < maxLength) || (relLen + g_virtualWorkingDir.count <= maxLength))
{
StringBuffer<> wres;
FixPath(wres, wideRelPath.data);
sprintf_s(absPath, maxLength, "%ls", wres.data);
UBA_ASSERT(wres.count < maxLength);
res = absPath;
}
DEBUG_LOG_DETOURED(L"_fullpath", L"(%hs) -> %hs", relPath, res);
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess && !g_runningRemote)
{
char otherBuf[1024] = {};
SuppressDetourScope _;
char* test = True__fullpath(otherBuf, relPath, maxLength); (void)test;
UBA_ASSERT((res != nullptr) == (test != nullptr) && (!res || (strcmp(res, test) == 0 || strchr(test, '~'))));
}
#endif
return res;
}
errno_t Detoured__get_wpgmptr(wchar_t** pValue)
{
DETOURED_CALL(_get_wpgmptr);
DEBUG_LOG_DETOURED(L"_get_wpgmptr", L"(%ls)", g_virtualApplication.data);
*pValue = (wchar_t*)g_virtualApplication.data;
return 0;
}
#if UBA_DEBUG_LOG_ENABLED
const wchar_t* WaccessResultToString(errno_t res)
{
switch (res)
{
case 0:
return L"Success";
case EACCES:
return L"AccessDenied";
case ENOENT:
return L"NotFound";
case EINVAL:
return L"InvalidParameter.";
default:
return L"UnknownError.";
}
}
#endif
errno_t Detoured__waccess_s(const wchar_t* path, int mode)
{
DETOURED_CALL(_waccess_s);
StringBuffer<> fixedPath;
FixPath(fixedPath, path);
const wchar_t* originalPath = path;(void)originalPath;
path = fixedPath.data;
if (g_runningRemote && fixedPath.StartsWith(g_exeDir.data))
{
StringBuffer<> temp;
temp.Append(g_virtualApplicationDir).Append(fixedPath.data + g_exeDir.count);
fixedPath.Clear().Append(temp);
}
if (!CanDetour(path))
{
auto res = True__waccess_s(path, mode);
DEBUG_LOG_TRUE(L"_waccess_s", L"(NODETOUR) %ls %i -> %ls", originalPath, mode, WaccessResultToString(res));
return res;
}
DevirtualizePath(fixedPath);
FileAttributes attr;
const wchar_t* realName = Shared_GetFileAttributes(attr, fixedPath);
if (!attr.useCache)
{
auto res = True__waccess_s(realName, mode);
DEBUG_LOG_TRUE(L"_waccess_s", L"(NOCACHE) %ls %i -> %ls", originalPath, mode, WaccessResultToString(res));
return res;
}
SetLastError(attr.lastError);
errno_t res = attr.exists ? 0 : ENOENT;
DEBUG_LOG_DETOURED(L"_waccess_s", L"%ls %i -> %ls", originalPath, mode, WaccessResultToString(res));
return res;
}
//int Detoured__wsopen_s(int* pfh, const wchar_t *filename, int oflag, int shflag, int pmode)
//{
// DEBUG_LOG_DETOURED(L"_wsopen_s", L"(%ls)", filename);
// int ret = True__wsopen_s(pfh, filename, oflag, shflag, pmode);
// return ret;
//}
//int Detoured__fileno(FILE *stream)
//{
// if (stream == stdin)
// return 0;
// if (stream == stdout)
// return 1;
// if (stream == stderr)
// return 3;
// return True__fileno(stream);
//}
intptr_t Detoured__get_osfhandle(int fd)
{
DETOURED_CALL(_get_osfhandle);
if (g_isDetachedProcess)
{
int fdTemp = fd;
if (fd == StdOutFd)
fdTemp = 1;
if (fdTemp >= 0 && fdTemp <= 2)
{
intptr_t res = intptr_t(g_stdHandle[fdTemp]);
DEBUG_LOG_DETOURED(L"_get_osfhandle", L"(%i) -> %llu", fd, res);
return res;
}
}
auto res = True__get_osfhandle(fd);
DEBUG_LOG_TRUE(L"_get_osfhandle", L"(%i) -> %llu", fd, u64(res));
return res;
}
#if !defined(_M_ARM64)
int Detoured__isatty(int fd)
{
DETOURED_CALL(_isatty);
if (fd == StdOutFd && g_isDetachedProcess) // Tell process stdout should go to file
return 1;
int res = True__isatty(fd);
// DEBUG_LOG_TRUE(L"_isatty", L"(%i) -> %i", fd, res); // Spam
return res;
}
#endif
int Detoured__write(int fd, const void* buffer, unsigned int count)
{
DETOURED_CALL(_write);
if (fd == StdOutFd && g_isDetachedProcess)
{
if (g_suppressLogging)
return count;
const char* str = (const char*)buffer;
const char* end = str + count;
const char* line = str;
do
{
const char* lineEnd = strchr(line, '\n');
if (!lineEnd)
lineEnd = end;
Rpc_WriteLogf(L"%.*hs", u32(lineEnd - line), line);
line = lineEnd + 1;
} while (line < end);
return count;
}
int res = True__write(fd, buffer, count);
// DEBUG_LOG_TRUE(L"_write", L"(%i) -> %i", fd, res); // Spam
return res;
}
int Detoured_fputs(const char* str, FILE* stream)
{
// This code is not working properly on wine helpers.
// When using iwyu on remote wine helper we get a fno that matches stderr but it is not a stderr.. it is supposed to write to a file
// Unfortunately I can't remember the initial reason this code was added so I can't change it and know if I broke something or not
if (!g_runningRemote || !g_isRunningWine)
{
int fno = _fileno(stream); (void)fno;
int errfno = _fileno(stderr);
if (fno == errfno || fno == _fileno(stdout))
{
Shared_WriteConsole(str, u32(strlen(str)), fno == errfno);
return 1;
}
}
return True_fputs(str, stream);
}
int Detoured__wspawnl(int mode, const wchar_t* cmdname, const wchar_t* arg0, const wchar_t* arg1, ...)
{
DETOURED_CALL(_wspawnl);
StringBuffer<32*1024> cmdLine; // Need long commandline for cmake/ninja
if (cmdname == nullptr)
cmdname = arg0;
// Unfortunately lib.exe is using _wpgmptr which means it will return a path that is bad
StringBuffer<260> cmdNameTemp;
if (g_runningRemote && StartsWith(cmdname, g_exeDir.data))
{
cmdNameTemp.Append(g_virtualApplicationDir);
cmdNameTemp.Append(cmdname + g_exeDir.count);
cmdname = cmdNameTemp.data;
}
// TODO: Not sure what the rule is here.. very confusing with CreateProcess having application in two places
cmdLine.Append('\"').Append(cmdname).Append('\"');
//wcscpy_s(cmdLine, 1024, arg0);
//wcscat_s(cmdLine, 1024, L" ");
//wcscat_s(cmdLine, 1024, arg1);
cmdLine.Append(arg1);
va_list args;
va_start(args, arg1);
while (wchar_t* argstr = va_arg(args, wchar_t*))
cmdLine.Append(' ').Append(argstr);
va_end(args);
DEBUG_LOG_DETOURED(L"_wspawnl", L"(%ls %ls)", cmdname, cmdLine.data);
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
DWORD flags = CREATE_NO_WINDOW;
if (!Detoured_CreateProcessW(cmdname, cmdLine.data, NULL, NULL, false, flags, NULL, NULL, &si, &pi))
{
UBA_ASSERT(false);
return -1;
}
auto g = MakeGuard([&]() { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); });
DWORD res = Detoured_WaitForSingleObject(pi.hProcess, INFINITE);
if (res != WAIT_OBJECT_0)
{
UBA_ASSERT(false);
return -1;
}
DWORD exitCode = 0;
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
{
UBA_ASSERT(false);
return -1;
}
return exitCode;
}
#if !defined(__clang__)
errno_t Detoured__wsplitpath_s(const wchar_t* path, wchar_t* drive, size_t driveNumberOfElements, wchar_t* dir, size_t dirNumberOfElements, wchar_t* fname, size_t nameNumberOfElements, wchar_t* ext, size_t extNumberOfElements)
{
DETOURED_CALL(_wsplitpath_s);
g_rules->RepairMalformedLibPath(path);
auto res = True__wsplitpath_s(path, drive, driveNumberOfElements, dir, dirNumberOfElements, fname, nameNumberOfElements, ext, extNumberOfElements);
//DEBUG_LOG_TRUE(L"_wsplitpath_s", L"%ls %ls %ls %ls %ls", path, drive, dir, fname, ext);
return res;
}
#endif
#if defined(DETOURED_INCLUDE_DEBUG)
int Detoured__wcsnicoll_l(const wchar_t* string1, const wchar_t* string2, size_t count, _locale_t locale)
{
DETOURED_CALL(_wcsnicoll_l);
return True__wcsnicoll_l(string1, string2, count, locale);
/*
//DETOURED_SCOPE;
auto res = _wcsnicmp(string1, string2, count);
if (res < 0)
res = -1;
else if (res > 0)
res = 1;
#if UBA_DEBUG_VALIDATE
auto res2 = True__wcsnicoll_l(string1, string2, count, locale);
if (res2 < 0)
res2 = -1;
else if (res2 > 0)
res2 = 1;
UBA_ASSERT (res == res2);
#endif
return res;
*/
}
wchar_t* Detoured__wgetenv(const wchar_t* varname)
{
DETOURED_CALL(_wgetenv);
auto res = True__wgetenv(varname);
DEBUG_LOG_DETOURED(L"_wgetenv", L"%ls -> %ls", varname, res);
return res;
}
errno_t Detoured__wgetenv_s(size_t* pReturnValue, wchar_t* buffer, size_t numberOfElements, const wchar_t* varname)
{
DETOURED_CALL(_wgetenv);
auto res = True__wgetenv_s(pReturnValue, buffer, numberOfElements, varname);
DEBUG_LOG_DETOURED(L"_wgetenv_s", L"%ls -> %ls", varname, buffer);
return res;
}
char* Detoured_getenv(const char* varname)
{
DETOURED_CALL(getenv);
auto res = True_getenv(varname);
DEBUG_LOG_DETOURED(L"getenv", L"%hs -> %hs", varname, res);
return res;
}
errno_t Detoured_getenv_s(size_t* pReturnValue, char* buffer, size_t numberOfElements, const char* varname)
{
DETOURED_CALL(getenv_s);
auto res = True_getenv_s(pReturnValue, buffer, numberOfElements, varname);
DEBUG_LOG_DETOURED(L"getenv_s", L"%hs -> %hs", varname, buffer);
return res;
}
errno_t Detoured__wmakepath_s(wchar_t* path, size_t sizeInWords, const wchar_t* drive, const wchar_t* dir, const wchar_t* fname, const wchar_t* ext)
{
DETOURED_CALL(_wmakepath_s);
auto res = True__wmakepath_s(path, sizeInWords, drive, dir, fname, ext);
DEBUG_LOG_TRUE(L"_wmakepath_s", L"%ls %ls %ls %ls %ls", path, drive, dir, fname, ext);
return res;
}
char* Detoured__getcwd(char* buffer, int maxlen)
{
DETOURED_CALL(_getcwd);
auto res = True__getcwd(buffer, maxlen); // We know this calls GetCommandLine for both wine and windows
DEBUG_LOG_TRUE(L"_getcwd", L"%hs", res);
return res;
}
#endif // defined(DETOURED_INCLUDE_DEBUG)