428 lines
12 KiB
C++
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)
|