mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 15:15:10 +00:00
Improve DLL loading and process launch handling
This commit is contained in:
parent
042a43ced1
commit
b4ea1da959
@ -7,34 +7,34 @@ namespace advapi32 {
|
||||
return 1; // screw them for now
|
||||
}
|
||||
|
||||
bool WIN_FUNC CryptReleaseContext(void* hProv, unsigned int dwFlags) {
|
||||
BOOL WIN_FUNC CryptReleaseContext(void* hProv, unsigned int dwFlags) {
|
||||
DEBUG_LOG("STUB: CryptReleaseContext %p %u\n", hProv, dwFlags);
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool WIN_FUNC CryptAcquireContextW(void** phProv, const wchar_t* pszContainer, const wchar_t* pszProvider, unsigned int dwProvType, unsigned int dwFlags){
|
||||
BOOL WIN_FUNC CryptAcquireContextW(void** phProv, const wchar_t* pszContainer, const wchar_t* pszProvider, unsigned int dwProvType, unsigned int dwFlags){
|
||||
DEBUG_LOG("STUB: CryptAcquireContextW(%p)\n", phProv);
|
||||
|
||||
// to quote the guy above me: screw them for now
|
||||
static int lmao = 42;
|
||||
if (phProv) {
|
||||
*phProv = &lmao;
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool WIN_FUNC CryptGenRandom(void* hProv, unsigned int dwLen, unsigned char* pbBuffer){
|
||||
BOOL WIN_FUNC CryptGenRandom(void* hProv, unsigned int dwLen, unsigned char* pbBuffer){
|
||||
DEBUG_LOG("STUB: CryptGenRandom(%p)\n", hProv);
|
||||
if (!pbBuffer || dwLen == 0) return false;
|
||||
if (!pbBuffer || dwLen == 0) return FALSE;
|
||||
|
||||
ssize_t ret = getrandom(pbBuffer, dwLen, 0);
|
||||
if (ret < 0 || (size_t)ret != dwLen) {
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
167
dll/kernel32.cpp
167
dll/kernel32.cpp
@ -12,6 +12,7 @@
|
||||
#include <filesystem>
|
||||
#include <fnmatch.h>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include "strutil.h"
|
||||
#include <malloc.h>
|
||||
#include <random>
|
||||
@ -318,8 +319,8 @@ namespace kernel32 {
|
||||
PROCESS_INFORMATION *lpProcessInformation
|
||||
) {
|
||||
DEBUG_LOG("CreateProcessA %s \"%s\" %p %p %d 0x%x %p %s %p %p\n",
|
||||
lpApplicationName,
|
||||
lpCommandLine,
|
||||
lpApplicationName ? lpApplicationName : "<null>",
|
||||
lpCommandLine ? lpCommandLine : "<null>",
|
||||
lpProcessAttributes,
|
||||
lpThreadAttributes,
|
||||
bInheritHandles,
|
||||
@ -330,63 +331,46 @@ namespace kernel32 {
|
||||
lpProcessInformation
|
||||
);
|
||||
|
||||
// Argument parsing
|
||||
// First: how many arguments do we have?
|
||||
size_t argc = 2;
|
||||
|
||||
for (size_t i = 1; i < strlen(lpCommandLine); i++) {
|
||||
if (isspace(lpCommandLine[i]) && !isspace(lpCommandLine[i - 1]))
|
||||
argc++;
|
||||
}
|
||||
|
||||
char **argv = (char **) calloc(argc + 1, sizeof(char*));
|
||||
argv[0] = wibo::executableName;
|
||||
std::string pathStr = files::pathFromWindows(lpApplicationName).string();
|
||||
argv[1] = (char *) pathStr.c_str();
|
||||
|
||||
char* arg = strtok(lpCommandLine, " ");
|
||||
size_t current_arg_index = 2;
|
||||
|
||||
while (arg != NULL) {
|
||||
// We're deliberately discarding the first token here
|
||||
// to prevent from doubling up on the target executable name
|
||||
// (it appears as lpApplicationName, and as the first token in lpCommandLine)
|
||||
arg = strtok(NULL, " ");
|
||||
|
||||
if (arg) {
|
||||
// Trim all quotation marks from the start and the end of the string
|
||||
while(*arg == '\"') {
|
||||
arg++;
|
||||
}
|
||||
|
||||
char* end = arg + strlen(arg) - 1;
|
||||
while(end > arg && *end == '\"') {
|
||||
*end = '\0';
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
argv[current_arg_index++] = arg;
|
||||
}
|
||||
|
||||
argv[argc] = NULL; // Last element in argv should be a null pointer
|
||||
|
||||
// YET TODO: take into account process / thread attributes, environment variables
|
||||
// working directory, etc.
|
||||
setenv("WIBO_DEBUG_INDENT", std::to_string(wibo::debugIndent + 1).c_str(), true);
|
||||
|
||||
pid_t pid;
|
||||
if (posix_spawn(&pid, wibo::executableName, NULL, NULL, argv, environ)) {
|
||||
std::string application = lpApplicationName ? lpApplicationName : "";
|
||||
std::vector<std::string> arguments = processes::splitCommandLine(lpCommandLine);
|
||||
if (application.empty()) {
|
||||
if (arguments.empty()) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
application = arguments.front();
|
||||
}
|
||||
if (arguments.empty()) {
|
||||
arguments.push_back(application);
|
||||
}
|
||||
|
||||
*lpProcessInformation = {
|
||||
.hProcess = processes::allocProcessHandle(pid),
|
||||
.hThread = nullptr,
|
||||
.dwProcessId = (DWORD) pid,
|
||||
.dwThreadId = 42
|
||||
};
|
||||
auto resolved = processes::resolveExecutable(application, true);
|
||||
if (!resolved) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnViaWibo(*resolved, arguments, &pid);
|
||||
if (spawnResult != 0) {
|
||||
wibo::lastError = (spawnResult == ENOENT) ? ERROR_FILE_NOT_FOUND : ERROR_ACCESS_DENIED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lpProcessInformation) {
|
||||
lpProcessInformation->hProcess = processes::allocProcessHandle(pid);
|
||||
lpProcessInformation->hThread = nullptr;
|
||||
lpProcessInformation->dwProcessId = static_cast<DWORD>(pid);
|
||||
lpProcessInformation->dwThreadId = 0;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
(void)lpProcessAttributes;
|
||||
(void)lpThreadAttributes;
|
||||
(void)bInheritHandles;
|
||||
(void)dwCreationFlags;
|
||||
(void)lpEnvironment;
|
||||
(void)lpCurrentDirectory;
|
||||
(void)lpStartupInfo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1918,6 +1902,15 @@ namespace kernel32 {
|
||||
return wibo::loadModule(lpLibFileName);
|
||||
}
|
||||
|
||||
HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) {
|
||||
DEBUG_LOG("LoadLibraryW\n");
|
||||
if (!lpLibFileName) {
|
||||
return nullptr;
|
||||
}
|
||||
auto filename = wideStringToString(lpLibFileName);
|
||||
return LoadLibraryA(filename.c_str());
|
||||
}
|
||||
|
||||
HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) {
|
||||
assert(!hFile);
|
||||
DEBUG_LOG("LoadLibraryExW(%x) -> ", dwFlags);
|
||||
@ -2554,35 +2547,74 @@ namespace kernel32 {
|
||||
return 0; // fail
|
||||
}
|
||||
|
||||
|
||||
static std::string convertEnvValueForWindows(const std::string &name, const char *rawValue) {
|
||||
if (!rawValue) {
|
||||
return std::string();
|
||||
}
|
||||
if (strcasecmp(name.c_str(), "PATH") != 0) {
|
||||
return rawValue;
|
||||
}
|
||||
std::string converted = files::hostPathListToWindows(rawValue);
|
||||
return converted.empty() ? std::string(rawValue) : converted;
|
||||
}
|
||||
|
||||
static std::string convertEnvValueToHost(const std::string &name, const char *rawValue) {
|
||||
if (!rawValue) {
|
||||
return std::string();
|
||||
}
|
||||
if (strcasecmp(name.c_str(), "PATH") != 0) {
|
||||
return rawValue;
|
||||
}
|
||||
std::string converted = files::windowsPathListToHost(rawValue);
|
||||
return converted.empty() ? std::string(rawValue) : converted;
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) {
|
||||
DEBUG_LOG("GetEnvironmentVariableA: %s\n", lpName);
|
||||
const char *value = getenv(lpName);
|
||||
if (!value) {
|
||||
if (!lpName) {
|
||||
return 0;
|
||||
}
|
||||
unsigned int len = strlen(value);
|
||||
const char *rawValue = getenv(lpName);
|
||||
if (!rawValue) {
|
||||
return 0;
|
||||
}
|
||||
std::string converted = convertEnvValueForWindows(lpName, rawValue);
|
||||
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
|
||||
unsigned int len = finalValue.size();
|
||||
if (nSize == 0) {
|
||||
return len + 1;
|
||||
}
|
||||
if (nSize < len) {
|
||||
if (nSize <= len) {
|
||||
return len;
|
||||
}
|
||||
memcpy(lpBuffer, value, len + 1);
|
||||
memcpy(lpBuffer, finalValue.c_str(), len + 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC SetEnvironmentVariableA(const char *lpName, const char *lpValue) {
|
||||
DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName, lpValue);
|
||||
return setenv(lpName, lpValue, 1 /* OVERWRITE */);
|
||||
DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName, lpValue ? lpValue : "<null>");
|
||||
if (!lpName) {
|
||||
return 0;
|
||||
}
|
||||
if (!lpValue) {
|
||||
return unsetenv(lpName);
|
||||
}
|
||||
std::string hostValue = convertEnvValueToHost(lpName, lpValue);
|
||||
const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str();
|
||||
return setenv(lpName, valuePtr, 1 /* OVERWRITE */);
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) {
|
||||
DEBUG_LOG("GetEnvironmentVariableW: %s\n", wideStringToString(lpName).c_str());
|
||||
const char *value = getenv(wideStringToString(lpName).c_str());
|
||||
if (!value) {
|
||||
std::string name = wideStringToString(lpName);
|
||||
DEBUG_LOG("GetEnvironmentVariableW: %s\n", name.c_str());
|
||||
const char *rawValue = getenv(name.c_str());
|
||||
if (!rawValue) {
|
||||
return 0;
|
||||
}
|
||||
auto wideValue = stringToWideString(value);
|
||||
std::string converted = convertEnvValueForWindows(name, rawValue);
|
||||
const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted;
|
||||
auto wideValue = stringToWideString(finalValue.c_str());
|
||||
const auto len = wideValue.size();
|
||||
if (nSize < len) {
|
||||
return len;
|
||||
@ -2908,6 +2940,7 @@ static void *resolveByName(const char *name) {
|
||||
if (strcmp(name, "LockResource") == 0) return (void *) kernel32::LockResource;
|
||||
if (strcmp(name, "SizeofResource") == 0) return (void *) kernel32::SizeofResource;
|
||||
if (strcmp(name, "LoadLibraryA") == 0) return (void *) kernel32::LoadLibraryA;
|
||||
if (strcmp(name, "LoadLibraryW") == 0) return (void *) kernel32::LoadLibraryW;
|
||||
if (strcmp(name, "LoadLibraryExW") == 0) return (void *) kernel32::LoadLibraryExW;
|
||||
if (strcmp(name, "DisableThreadLibraryCalls") == 0) return (void *) kernel32::DisableThreadLibraryCalls;
|
||||
if (strcmp(name, "FreeLibrary") == 0) return (void *) kernel32::FreeLibrary;
|
||||
|
507
dll/msvcrt.cpp
507
dll/msvcrt.cpp
@ -11,19 +11,27 @@
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
#include "files.h"
|
||||
#include "processes.h"
|
||||
#include "strutil.h"
|
||||
|
||||
typedef void (*_PVFV)();
|
||||
typedef int (*_PIFV)();
|
||||
using _onexit_t = _PIFV;
|
||||
|
||||
extern "C" char **environ;
|
||||
|
||||
namespace msvcrt {
|
||||
int _commode;
|
||||
int _fmode;
|
||||
@ -66,6 +74,60 @@ namespace msvcrt {
|
||||
return tables.back();
|
||||
}
|
||||
|
||||
std::string normalizeEnvStringForWindows(const char *src) {
|
||||
if (!src) {
|
||||
return std::string();
|
||||
}
|
||||
std::string entry(src);
|
||||
auto pos = entry.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
return entry;
|
||||
}
|
||||
std::string name = entry.substr(0, pos);
|
||||
std::string value = entry.substr(pos + 1);
|
||||
if (strcasecmp(name.c_str(), "PATH") == 0) {
|
||||
std::string converted = files::hostPathListToWindows(value);
|
||||
std::string result = converted.empty() ? value : converted;
|
||||
std::string exeDir;
|
||||
if (wibo::argv && wibo::argv[0]) {
|
||||
std::filesystem::path exePath = std::filesystem::absolute(std::filesystem::path(wibo::argv[0])).parent_path();
|
||||
if (!exePath.empty()) {
|
||||
exeDir = files::pathToWindows(exePath);
|
||||
}
|
||||
}
|
||||
if (!exeDir.empty()) {
|
||||
std::string loweredResult = result;
|
||||
std::string loweredExe = exeDir;
|
||||
std::transform(loweredResult.begin(), loweredResult.end(), loweredResult.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
std::transform(loweredExe.begin(), loweredExe.end(), loweredExe.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
bool present = false;
|
||||
size_t start = 0;
|
||||
while (start <= loweredResult.size()) {
|
||||
size_t end = loweredResult.find(';', start);
|
||||
if (end == std::string::npos) {
|
||||
end = loweredResult.size();
|
||||
}
|
||||
if (loweredResult.substr(start, end - start) == loweredExe) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
if (end == loweredResult.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
if (!present) {
|
||||
if (!result.empty() && result.back() != ';') {
|
||||
result.push_back(';');
|
||||
}
|
||||
result += exeDir;
|
||||
}
|
||||
}
|
||||
entry = name + "=" + result;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
struct StringListStorage {
|
||||
std::vector<std::unique_ptr<CharT[]>> strings;
|
||||
@ -103,23 +165,19 @@ namespace msvcrt {
|
||||
};
|
||||
|
||||
std::vector<char> copyNarrowString(const char *src) {
|
||||
if (!src) {
|
||||
src = "";
|
||||
}
|
||||
size_t len = std::strlen(src);
|
||||
std::string normalized = normalizeEnvStringForWindows(src);
|
||||
size_t len = normalized.size();
|
||||
std::vector<char> result(len + 1);
|
||||
if (len > 0) {
|
||||
std::memcpy(result.data(), src, len);
|
||||
std::memcpy(result.data(), normalized.data(), len);
|
||||
}
|
||||
result[len] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> copyWideString(const char *src) {
|
||||
if (!src) {
|
||||
src = "";
|
||||
}
|
||||
return stringToWideString(src);
|
||||
std::string normalized = normalizeEnvStringForWindows(src);
|
||||
return stringToWideString(normalized.c_str());
|
||||
}
|
||||
|
||||
template <typename CharT, typename Converter>
|
||||
@ -427,6 +485,90 @@ namespace msvcrt {
|
||||
std::free(ptr);
|
||||
}
|
||||
|
||||
void* WIN_ENTRY memcpy(void *dest, const void *src, size_t count) {
|
||||
return std::memcpy(dest, src, count);
|
||||
}
|
||||
|
||||
void* WIN_ENTRY memmove(void *dest, const void *src, size_t count) {
|
||||
return std::memmove(dest, src, count);
|
||||
}
|
||||
|
||||
int WIN_ENTRY fflush(FILE *stream) {
|
||||
return std::fflush(stream);
|
||||
}
|
||||
|
||||
FILE *WIN_ENTRY fopen(const char *filename, const char *mode) {
|
||||
return std::fopen(filename, mode);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _dup2(int fd1, int fd2) {
|
||||
return dup2(fd1, fd2);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _isatty(int fd) {
|
||||
return isatty(fd);
|
||||
}
|
||||
|
||||
int WIN_ENTRY fseek(FILE *stream, long offset, int origin) {
|
||||
return std::fseek(stream, offset, origin);
|
||||
}
|
||||
|
||||
long WIN_ENTRY ftell(FILE *stream) {
|
||||
return std::ftell(stream);
|
||||
}
|
||||
|
||||
int WIN_ENTRY feof(FILE *stream) {
|
||||
return std::feof(stream);
|
||||
}
|
||||
|
||||
int WIN_ENTRY fputws(const uint16_t *str, FILE *stream) {
|
||||
std::wstring temp;
|
||||
if (str) {
|
||||
for (const uint16_t *cursor = str; *cursor; ++cursor) {
|
||||
temp.push_back(static_cast<wchar_t>(*cursor));
|
||||
}
|
||||
}
|
||||
return std::fputws(temp.c_str(), stream);
|
||||
}
|
||||
|
||||
uint16_t* WIN_ENTRY fgetws(uint16_t *buffer, int size, FILE *stream) {
|
||||
if (!buffer || size <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<wchar_t> temp(static_cast<size_t>(size));
|
||||
wchar_t *res = std::fgetws(temp.data(), size, stream);
|
||||
if (!res) {
|
||||
return nullptr;
|
||||
}
|
||||
for (int i = 0; i < size; ++i) {
|
||||
buffer[i] = static_cast<uint16_t>(temp[i]);
|
||||
if (temp[i] == L'\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
wint_t WIN_ENTRY fgetwc(FILE *stream) {
|
||||
return std::fgetwc(stream);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wfopen_s(FILE **stream, const uint16_t *filename, const uint16_t *mode) {
|
||||
if (!stream || !filename || !mode) {
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
std::string narrowName = wideStringToString(filename);
|
||||
std::string narrowMode = wideStringToString(mode);
|
||||
FILE *handle = std::fopen(narrowName.c_str(), narrowMode.c_str());
|
||||
if (!handle) {
|
||||
*stream = nullptr;
|
||||
return errno ? errno : EINVAL;
|
||||
}
|
||||
*stream = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t toLower(uint16_t ch) {
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
return static_cast<uint16_t>(ch + ('a' - 'A'));
|
||||
@ -463,6 +605,234 @@ namespace msvcrt {
|
||||
return static_cast<int>(a) - static_cast<int>(b);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wmakepath_s(uint16_t *path, size_t sizeInWords, const uint16_t *drive, const uint16_t *dir,
|
||||
const uint16_t *fname, const uint16_t *ext) {
|
||||
if (!path || sizeInWords == 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
path[0] = 0;
|
||||
std::u16string result;
|
||||
|
||||
auto append = [&](const uint16_t *src) {
|
||||
if (!src || !*src) {
|
||||
return;
|
||||
}
|
||||
for (const uint16_t *cursor = src; *cursor; ++cursor) {
|
||||
result.push_back(static_cast<char16_t>(*cursor));
|
||||
}
|
||||
};
|
||||
|
||||
if (drive && *drive) {
|
||||
result.push_back(static_cast<char16_t>(drive[0]));
|
||||
if (drive[1] == u':') {
|
||||
result.push_back(u':');
|
||||
append(drive + 2);
|
||||
} else {
|
||||
result.push_back(u':');
|
||||
append(drive + 1);
|
||||
}
|
||||
}
|
||||
|
||||
auto appendDir = [&](const uint16_t *directory) {
|
||||
if (!directory || !*directory) {
|
||||
return;
|
||||
}
|
||||
append(directory);
|
||||
if (result.empty()) {
|
||||
return;
|
||||
}
|
||||
char16_t last = result.back();
|
||||
if (last != u'/' && last != u'\\') {
|
||||
result.push_back(u'\\');
|
||||
}
|
||||
};
|
||||
|
||||
appendDir(dir);
|
||||
append(fname);
|
||||
|
||||
if (ext && *ext) {
|
||||
if (*ext != u'.') {
|
||||
result.push_back(u'.');
|
||||
append(ext);
|
||||
} else {
|
||||
append(ext);
|
||||
}
|
||||
}
|
||||
|
||||
size_t required = result.size() + 1;
|
||||
if (required > sizeInWords) {
|
||||
path[0] = 0;
|
||||
return ERANGE;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < result.size(); ++i) {
|
||||
path[i] = static_cast<uint16_t>(result[i]);
|
||||
}
|
||||
path[result.size()] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wputenv_s(const uint16_t *varname, const uint16_t *value) {
|
||||
if (!varname || !value) {
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (!*varname) {
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (const uint16_t *cursor = varname; *cursor; ++cursor) {
|
||||
if (*cursor == static_cast<uint16_t>('=')) {
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = wideStringToString(varname);
|
||||
if (name.empty()) {
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int resultCode = 0;
|
||||
if (!*value) {
|
||||
if (unsetenv(name.c_str()) != 0) {
|
||||
resultCode = errno != 0 ? errno : EINVAL;
|
||||
}
|
||||
} else {
|
||||
std::string narrowValue = wideStringToString(value);
|
||||
if (setenv(name.c_str(), narrowValue.c_str(), 1) != 0) {
|
||||
resultCode = errno != 0 ? errno : EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultCode != 0) {
|
||||
errno = resultCode;
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
getMainArgsCommon<char>(nullptr, nullptr, nullptr, copyNarrowString);
|
||||
getMainArgsCommon<uint16_t>(nullptr, nullptr, nullptr, copyWideString);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long WIN_ENTRY wcsspn(const uint16_t *str1, const uint16_t *str2) {
|
||||
if (!str1 || !str2) {
|
||||
return 0;
|
||||
}
|
||||
unsigned long count = 0;
|
||||
for (const uint16_t *p = str1; *p; ++p) {
|
||||
bool match = false;
|
||||
for (const uint16_t *q = str2; *q; ++q) {
|
||||
if (*p == *q) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
long WIN_ENTRY _wtol(const uint16_t *str) {
|
||||
return wstrtol(str, nullptr, 10);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wcsupr_s(uint16_t *str, size_t size) {
|
||||
if (!str || size == 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
size_t len = wstrnlen(str, size);
|
||||
if (len >= size) {
|
||||
return ERANGE;
|
||||
}
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
wchar_t ch = static_cast<wchar_t>(str[i]);
|
||||
str[i] = static_cast<uint16_t>(std::towupper(ch));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wcslwr_s(uint16_t *str, size_t size) {
|
||||
if (!str || size == 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
size_t len = wstrnlen(str, size);
|
||||
if (len >= size) {
|
||||
return ERANGE;
|
||||
}
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
wchar_t ch = static_cast<wchar_t>(str[i]);
|
||||
str[i] = static_cast<uint16_t>(std::towlower(ch));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
wint_t WIN_ENTRY towlower(wint_t ch) {
|
||||
return static_cast<wint_t>(std::towlower(static_cast<wchar_t>(ch)));
|
||||
}
|
||||
|
||||
int WIN_ENTRY _ftime64_s(void *timeb) {
|
||||
DEBUG_LOG("STUB: _ftime64_s(%p)\n", timeb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _crt_debugger_hook(int value) {
|
||||
DEBUG_LOG("_crt_debugger_hook(%d)\n", value);
|
||||
(void)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _configthreadlocale(int mode) {
|
||||
static int currentMode = 0;
|
||||
int previous = currentMode;
|
||||
if (mode == -1) {
|
||||
return previous;
|
||||
}
|
||||
if (mode == 0 || mode == 1 || mode == 2) {
|
||||
currentMode = mode;
|
||||
return previous;
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void abort_and_log(const char *reason) {
|
||||
DEBUG_LOG("Runtime abort: %s\n", reason ? reason : "");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int WIN_ENTRY _amsg_exit(int reason) {
|
||||
DEBUG_LOG("_amsg_exit(%d)\n", reason);
|
||||
abort_and_log("_amsg_exit");
|
||||
return reason;
|
||||
}
|
||||
|
||||
void WIN_ENTRY _invoke_watson(const uint16_t *, const uint16_t *, const uint16_t *, unsigned int, uintptr_t) {
|
||||
DEBUG_LOG("_invoke_watson\n");
|
||||
abort_and_log("_invoke_watson");
|
||||
}
|
||||
|
||||
void WIN_ENTRY terminateShim() {
|
||||
abort_and_log("terminate");
|
||||
}
|
||||
|
||||
int WIN_ENTRY _except_handler4_common(void *, void *, void *, void *) {
|
||||
DEBUG_LOG("_except_handler4_common\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long WIN_ENTRY _XcptFilter(unsigned long code, void *) {
|
||||
DEBUG_LOG("_XcptFilter(%lu)\n", code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _get_wpgmptr(uint16_t** pValue){
|
||||
DEBUG_LOG("_get_wpgmptr(%p)\n", pValue);
|
||||
if(!pValue) return 22;
|
||||
@ -576,9 +946,17 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
int WIN_ENTRY _waccess_s(const uint16_t* path, int mode){
|
||||
std::string str = wideStringToString(path);
|
||||
DEBUG_LOG("_waccess_s %s\n", str.c_str());
|
||||
return access(str.c_str(), mode);
|
||||
std::string original = wideStringToString(path);
|
||||
DEBUG_LOG("_waccess_s %s\n", original.c_str());
|
||||
std::filesystem::path host = files::pathFromWindows(original.c_str());
|
||||
std::string candidate;
|
||||
if (!host.empty()) {
|
||||
candidate = host.string();
|
||||
} else {
|
||||
candidate = original;
|
||||
std::replace(candidate.begin(), candidate.end(), '\\', '/');
|
||||
}
|
||||
return access(candidate.c_str(), mode);
|
||||
}
|
||||
|
||||
void* WIN_ENTRY memset(void *s, int c, size_t n){
|
||||
@ -744,29 +1122,16 @@ namespace msvcrt {
|
||||
return wstrtoul(strSource, endptr, base);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _dup2(int fd1, int fd2){
|
||||
return dup2(fd1, fd2);
|
||||
}
|
||||
|
||||
FILE* WIN_ENTRY _wfsopen(const uint16_t* filename, const uint16_t* mode, int shflag){
|
||||
if (!filename || !mode) return nullptr;
|
||||
std::string fname_str = wideStringToString(filename);
|
||||
std::string mode_str = wideStringToString(mode);
|
||||
DEBUG_LOG("_wfsopen file %s, mode %s\n", fname_str.c_str(), mode_str.c_str());
|
||||
|
||||
(void)shflag;
|
||||
return fopen(fname_str.c_str(), mode_str.c_str());
|
||||
}
|
||||
|
||||
int WIN_ENTRY fputws(const uint16_t* str, FILE* stream){
|
||||
if(!str || !stream) return EOF;
|
||||
|
||||
std::string fname_str = wideStringToString(str);
|
||||
DEBUG_LOG("fputws %s\n", fname_str.c_str());
|
||||
|
||||
if(fputs(fname_str.c_str(), stream) < 0) return EOF;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY puts(const char *str) {
|
||||
if (!str) {
|
||||
str = "(null)";
|
||||
@ -787,9 +1152,9 @@ namespace msvcrt {
|
||||
DEBUG_LOG("flushall\n");
|
||||
int count = 0;
|
||||
|
||||
if (fflush(stdin) == 0) count++;
|
||||
if (fflush(stdout) == 0) count++;
|
||||
if (fflush(stderr) == 0) count++;
|
||||
if (msvcrt::fflush(stdin) == 0) count++;
|
||||
if (msvcrt::fflush(stdout) == 0) count++;
|
||||
if (msvcrt::fflush(stderr) == 0) count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -799,11 +1164,63 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
intptr_t WIN_ENTRY _wspawnvp(int mode, const uint16_t* cmdname, const uint16_t* const * argv) {
|
||||
std::string str_cmd = wideStringToString(cmdname);
|
||||
DEBUG_LOG("STUB: _wspawnvp %s\n", str_cmd.c_str());
|
||||
if (!cmdname || !argv) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string command = wideStringToString(cmdname);
|
||||
DEBUG_LOG("_wspawnvp(mode=%d, cmd=%s)\n", mode, command.c_str());
|
||||
|
||||
std::vector<std::string> argStorage;
|
||||
for (const uint16_t *const *cursor = argv; *cursor; ++cursor) {
|
||||
argStorage.emplace_back(wideStringToString(*cursor));
|
||||
}
|
||||
if (argStorage.empty()) {
|
||||
argStorage.emplace_back(command);
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(command, true);
|
||||
if (!resolved) {
|
||||
errno = ENOENT;
|
||||
DEBUG_LOG("\tfailed to resolve executable for %s\n", command.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnViaWibo(*resolved, argStorage, &pid);
|
||||
if (spawnResult != 0) {
|
||||
errno = spawnResult;
|
||||
DEBUG_LOG("\tspawnViaWibo failed: %d\n", spawnResult);
|
||||
return -1;
|
||||
}
|
||||
|
||||
constexpr int P_WAIT = 0;
|
||||
constexpr int P_DETACH = 2;
|
||||
|
||||
if (mode == P_WAIT) {
|
||||
int status = 0;
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
DEBUG_LOG("\twaitpid failed: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
return static_cast<intptr_t>(WEXITSTATUS(status));
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
errno = EINTR;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode == P_DETACH) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _P_NOWAIT and unknown flags: return process id
|
||||
return static_cast<intptr_t>(pid);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wunlink(const uint16_t *filename){
|
||||
std::string str = wideStringToString(filename);
|
||||
DEBUG_LOG("_wunlink %s\n", str.c_str());
|
||||
@ -889,11 +1306,39 @@ static void *resolveByName(const char *name) {
|
||||
if (strcmp(name, "__dllonexit") == 0) return (void*)msvcrt::__dllonexit;
|
||||
if (strcmp(name, "free") == 0) return (void*)msvcrt::free;
|
||||
if (strcmp(name, "_wcsicmp") == 0) return (void*)msvcrt::_wcsicmp;
|
||||
if (strcmp(name, "_wmakepath_s") == 0) return (void*)msvcrt::_wmakepath_s;
|
||||
if (strcmp(name, "_wputenv_s") == 0) return (void*)msvcrt::_wputenv_s;
|
||||
if (strcmp(name, "_get_wpgmptr") == 0) return (void*)msvcrt::_get_wpgmptr;
|
||||
if (strcmp(name, "_wsplitpath_s") == 0) return (void*)msvcrt::_wsplitpath_s;
|
||||
if (strcmp(name, "wcscat_s") == 0) return (void*)msvcrt::wcscat_s;
|
||||
if (strcmp(name, "_wcsdup") == 0) return (void*)msvcrt::_wcsdup;
|
||||
if (strcmp(name, "memset") == 0) return (void*)msvcrt::memset;
|
||||
if (strcmp(name, "memcpy") == 0) return (void*)msvcrt::memcpy;
|
||||
if (strcmp(name, "memmove") == 0) return (void*)msvcrt::memmove;
|
||||
if (strcmp(name, "fflush") == 0) return (void*)msvcrt::fflush;
|
||||
if (strcmp(name, "fopen") == 0) return (void*)msvcrt::fopen;
|
||||
if (strcmp(name, "fseek") == 0) return (void*)msvcrt::fseek;
|
||||
if (strcmp(name, "ftell") == 0) return (void*)msvcrt::ftell;
|
||||
if (strcmp(name, "feof") == 0) return (void*)msvcrt::feof;
|
||||
if (strcmp(name, "fgetws") == 0) return (void*)msvcrt::fgetws;
|
||||
if (strcmp(name, "fgetwc") == 0) return (void*)msvcrt::fgetwc;
|
||||
if (strcmp(name, "fputws") == 0) return (void*)msvcrt::fputws;
|
||||
if (strcmp(name, "_wfopen_s") == 0) return (void*)msvcrt::_wfopen_s;
|
||||
if (strcmp(name, "wcsspn") == 0) return (void*)msvcrt::wcsspn;
|
||||
if (strcmp(name, "_wtol") == 0) return (void*)msvcrt::_wtol;
|
||||
if (strcmp(name, "_wcsupr_s") == 0) return (void*)msvcrt::_wcsupr_s;
|
||||
if (strcmp(name, "_wcslwr_s") == 0) return (void*)msvcrt::_wcslwr_s;
|
||||
if (strcmp(name, "_dup2") == 0) return (void*)msvcrt::_dup2;
|
||||
if (strcmp(name, "_isatty") == 0) return (void*)msvcrt::_isatty;
|
||||
if (strcmp(name, "towlower") == 0) return (void*)msvcrt::towlower;
|
||||
if (strcmp(name, "_ftime64_s") == 0) return (void*)msvcrt::_ftime64_s;
|
||||
if (strcmp(name, "_crt_debugger_hook") == 0) return (void*)msvcrt::_crt_debugger_hook;
|
||||
if (strcmp(name, "_configthreadlocale") == 0) return (void*)msvcrt::_configthreadlocale;
|
||||
if (strcmp(name, "_amsg_exit") == 0) return (void*)msvcrt::_amsg_exit;
|
||||
if (strcmp(name, "_invoke_watson") == 0) return (void*)msvcrt::_invoke_watson;
|
||||
if (strcmp(name, "_except_handler4_common") == 0) return (void*)msvcrt::_except_handler4_common;
|
||||
if (strcmp(name, "_XcptFilter") == 0) return (void*)msvcrt::_XcptFilter;
|
||||
if (strcmp(name, "?terminate@@YAXXZ") == 0) return (void*)msvcrt::terminateShim;
|
||||
if (strcmp(name, "wcsncpy_s") == 0) return (void*)msvcrt::wcsncpy_s;
|
||||
if (strcmp(name, "wcsncat_s") == 0) return (void*)msvcrt::wcsncat_s;
|
||||
if (strcmp(name, "_itow_s") == 0) return (void*)msvcrt::_itow_s;
|
||||
|
120
files.cpp
120
files.cpp
@ -2,10 +2,58 @@
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <strings.h>
|
||||
#include <string>
|
||||
|
||||
namespace files {
|
||||
|
||||
static std::vector<std::string> splitList(const std::string &value, char delimiter) {
|
||||
std::vector<std::string> entries;
|
||||
size_t start = 0;
|
||||
while (start <= value.size()) {
|
||||
size_t end = value.find(delimiter, start);
|
||||
if (end == std::string::npos) {
|
||||
end = value.size();
|
||||
}
|
||||
entries.emplace_back(value.substr(start, end - start));
|
||||
if (end == value.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
static std::string toWindowsPathEntry(const std::string &entry) {
|
||||
if (entry.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
bool looksWindows = entry.find('\\') != std::string::npos ||
|
||||
(entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
|
||||
if (looksWindows) {
|
||||
std::string normalized = entry;
|
||||
std::replace(normalized.begin(), normalized.end(), '/', '\\');
|
||||
return normalized;
|
||||
}
|
||||
return pathToWindows(std::filesystem::path(entry));
|
||||
}
|
||||
|
||||
static std::string toHostPathEntry(const std::string &entry) {
|
||||
if (entry.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
auto converted = pathFromWindows(entry.c_str());
|
||||
if (!converted.empty()) {
|
||||
return converted.string();
|
||||
}
|
||||
std::string normalized = entry;
|
||||
std::replace(normalized.begin(), normalized.end(), '\\', '/');
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static void *stdinHandle;
|
||||
static void *stdoutHandle;
|
||||
static void *stderrHandle;
|
||||
@ -123,4 +171,76 @@ namespace files {
|
||||
stdoutHandle = allocFpHandle(stdout);
|
||||
stderrHandle = allocFpHandle(stderr);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||
const std::string &filename) {
|
||||
std::error_code ec;
|
||||
if (directory.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::string needle = filename;
|
||||
std::transform(needle.begin(), needle.end(), needle.begin(), [](unsigned char ch) { return std::tolower(ch); });
|
||||
for (const auto &entry : std::filesystem::directory_iterator(directory, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
std::string candidate = entry.path().filename().string();
|
||||
std::transform(candidate.begin(), candidate.end(), candidate.begin(), [](unsigned char ch) { return std::tolower(ch); });
|
||||
if (candidate == needle) {
|
||||
return canonicalPath(entry.path());
|
||||
}
|
||||
}
|
||||
auto direct = directory / filename;
|
||||
if (std::filesystem::exists(direct, ec)) {
|
||||
return canonicalPath(direct);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::filesystem::path canonicalPath(const std::filesystem::path &path) {
|
||||
std::error_code ec;
|
||||
auto canonical = std::filesystem::weakly_canonical(path, ec);
|
||||
if (!ec) {
|
||||
return canonical;
|
||||
}
|
||||
return std::filesystem::absolute(path);
|
||||
}
|
||||
|
||||
std::string hostPathListToWindows(const std::string &value) {
|
||||
if (value.empty()) {
|
||||
return value;
|
||||
}
|
||||
char delimiter = value.find(';') != std::string::npos ? ';' : ':';
|
||||
auto entries = splitList(value, delimiter);
|
||||
std::string result;
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
if (i != 0) {
|
||||
result.push_back(';');
|
||||
}
|
||||
if (!entries[i].empty()) {
|
||||
result += toWindowsPathEntry(entries[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string windowsPathListToHost(const std::string &value) {
|
||||
if (value.empty()) {
|
||||
return value;
|
||||
}
|
||||
auto entries = splitList(value, ';');
|
||||
std::string result;
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
if (i != 0) {
|
||||
result.push_back(':');
|
||||
}
|
||||
if (!entries[i].empty()) {
|
||||
result += toHostPathEntry(entries[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
5
files.h
5
files.h
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace files {
|
||||
@ -11,6 +12,10 @@ namespace files {
|
||||
void *getStdHandle(uint32_t nStdHandle);
|
||||
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
|
||||
void init();
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory, const std::string &filename);
|
||||
std::filesystem::path canonicalPath(const std::filesystem::path &path);
|
||||
std::string hostPathListToWindows(const std::string &value);
|
||||
std::string windowsPathListToHost(const std::string &value);
|
||||
}
|
||||
|
||||
inline bool endsWith(const std::string &str, const std::string &suffix) {
|
||||
|
@ -110,6 +110,8 @@ struct ModuleRegistry {
|
||||
std::optional<std::filesystem::path> dllDirectory;
|
||||
bool initialized = false;
|
||||
std::unordered_map<void *, wibo::ModuleInfo *> onExitTables;
|
||||
std::unordered_map<const wibo::Module *, std::vector<std::string>> builtinAliasLists;
|
||||
std::unordered_map<std::string, wibo::ModuleInfo *> builtinAliasMap;
|
||||
};
|
||||
|
||||
ModuleRegistry ®istry() {
|
||||
@ -180,32 +182,6 @@ std::string normalizedBaseKey(const ParsedModuleName &parsed) {
|
||||
return normalizeAlias(base);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||
const std::string &filename) {
|
||||
std::error_code ec;
|
||||
if (directory.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string lower = toLowerCopy(filename);
|
||||
for (const auto &entry : std::filesystem::directory_iterator(directory, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
const std::string candidate = toLowerCopy(entry.path().filename().string());
|
||||
if (candidate == lower) {
|
||||
return std::filesystem::canonical(entry.path(), ec);
|
||||
}
|
||||
}
|
||||
auto direct = directory / filename;
|
||||
if (std::filesystem::exists(direct, ec)) {
|
||||
return std::filesystem::canonical(direct, ec);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> combineAndFind(const std::filesystem::path &directory,
|
||||
const std::string &filename) {
|
||||
if (filename.empty()) {
|
||||
@ -214,16 +190,7 @@ std::optional<std::filesystem::path> combineAndFind(const std::filesystem::path
|
||||
if (directory.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return findCaseInsensitiveFile(directory, filename);
|
||||
}
|
||||
|
||||
std::filesystem::path canonicalPath(const std::filesystem::path &path) {
|
||||
std::error_code ec;
|
||||
auto canonical = std::filesystem::weakly_canonical(path, ec);
|
||||
if (!ec) {
|
||||
return canonical;
|
||||
}
|
||||
return std::filesystem::absolute(path);
|
||||
return files::findCaseInsensitiveFile(directory, filename);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> collectSearchDirectories(bool alteredSearchPath) {
|
||||
@ -306,10 +273,10 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
|
||||
auto combined = parsed.directory + "\\" + candidate;
|
||||
auto posixPath = files::pathFromWindows(combined.c_str());
|
||||
if (!posixPath.empty()) {
|
||||
auto resolved = findCaseInsensitiveFile(std::filesystem::path(posixPath).parent_path(),
|
||||
auto resolved = files::findCaseInsensitiveFile(std::filesystem::path(posixPath).parent_path(),
|
||||
std::filesystem::path(posixPath).filename().string());
|
||||
if (resolved) {
|
||||
return canonicalPath(*resolved);
|
||||
return files::canonicalPath(*resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,7 +288,7 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
|
||||
for (const auto &candidate : names) {
|
||||
auto resolved = combineAndFind(dir, candidate);
|
||||
if (resolved) {
|
||||
return canonicalPath(*resolved);
|
||||
return files::canonicalPath(*resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -330,7 +297,7 @@ std::optional<std::filesystem::path> resolveModuleOnDisk(const std::string &requ
|
||||
}
|
||||
|
||||
std::string storageKeyForPath(const std::filesystem::path &path) {
|
||||
return normalizeAlias(files::pathToWindows(canonicalPath(path)));
|
||||
return normalizeAlias(files::pathToWindows(files::canonicalPath(path)));
|
||||
}
|
||||
|
||||
std::string storageKeyForBuiltin(const std::string &normalizedName) { return normalizedName; }
|
||||
@ -349,7 +316,13 @@ void registerAlias(const std::string &alias, wibo::ModuleInfo *info) {
|
||||
return;
|
||||
}
|
||||
auto ® = registry();
|
||||
if (reg.modulesByAlias.find(alias) == reg.modulesByAlias.end()) {
|
||||
auto it = reg.modulesByAlias.find(alias);
|
||||
if (it == reg.modulesByAlias.end()) {
|
||||
reg.modulesByAlias[alias] = info;
|
||||
return;
|
||||
}
|
||||
// Prefer externally loaded modules over built-ins when both are present.
|
||||
if (it->second && it->second->module != nullptr && info->module == nullptr) {
|
||||
reg.modulesByAlias[alias] = info;
|
||||
}
|
||||
}
|
||||
@ -369,10 +342,20 @@ void registerBuiltinModule(const wibo::Module *module) {
|
||||
auto ® = registry();
|
||||
reg.modulesByKey[storageKey] = std::move(entry);
|
||||
|
||||
reg.builtinAliasLists[module] = {};
|
||||
auto &aliasList = reg.builtinAliasLists[module];
|
||||
for (size_t i = 0; module->names[i]; ++i) {
|
||||
registerAlias(normalizeAlias(module->names[i]), raw);
|
||||
std::string alias = normalizeAlias(module->names[i]);
|
||||
aliasList.push_back(alias);
|
||||
registerAlias(alias, raw);
|
||||
reg.builtinAliasMap[alias] = raw;
|
||||
ParsedModuleName parsed = parseModuleName(module->names[i]);
|
||||
registerAlias(normalizedBaseKey(parsed), raw);
|
||||
std::string baseAlias = normalizedBaseKey(parsed);
|
||||
if (baseAlias != alias) {
|
||||
aliasList.push_back(baseAlias);
|
||||
registerAlias(baseAlias, raw);
|
||||
reg.builtinAliasMap[baseAlias] = raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,7 +526,7 @@ void shutdownModuleRegistry() {
|
||||
ModuleInfo *moduleInfoFromHandle(HMODULE module) { return static_cast<ModuleInfo *>(module); }
|
||||
|
||||
void setDllDirectoryOverride(const std::filesystem::path &path) {
|
||||
auto canonical = canonicalPath(path);
|
||||
auto canonical = files::canonicalPath(path);
|
||||
std::lock_guard<std::recursive_mutex> lock(registry().mutex);
|
||||
registry().dllDirectory = canonical;
|
||||
}
|
||||
@ -645,50 +628,34 @@ HMODULE loadModule(const char *dllName) {
|
||||
ensureInitialized();
|
||||
|
||||
ParsedModuleName parsed = parseModuleName(requested);
|
||||
std::string alias = normalizedBaseKey(parsed);
|
||||
ModuleInfo *existing = findByAlias(alias);
|
||||
if (!existing) {
|
||||
existing = findByAlias(normalizeAlias(requested));
|
||||
}
|
||||
if (existing) {
|
||||
DEBUG_LOG(" found existing module alias %s\n", alias.c_str());
|
||||
if (existing->refCount != UINT_MAX) {
|
||||
existing->refCount++;
|
||||
}
|
||||
lastError = ERROR_SUCCESS;
|
||||
return existing;
|
||||
}
|
||||
|
||||
auto resolvedPath = resolveModuleOnDisk(requested, false);
|
||||
if (!resolvedPath) {
|
||||
DEBUG_LOG(" module not found on disk\n");
|
||||
lastError = ERROR_MOD_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string key = storageKeyForPath(*resolvedPath);
|
||||
auto ® = registry();
|
||||
auto it = reg.modulesByKey.find(key);
|
||||
if (it != reg.modulesByKey.end()) {
|
||||
ModuleInfo *info = it->second.get();
|
||||
DWORD diskError = ERROR_SUCCESS;
|
||||
|
||||
auto tryLoadExternal = [&](const std::filesystem::path &path) -> ModuleInfo * {
|
||||
std::string key = storageKeyForPath(path);
|
||||
auto existingIt = reg.modulesByKey.find(key);
|
||||
if (existingIt != reg.modulesByKey.end()) {
|
||||
ModuleInfo *info = existingIt->second.get();
|
||||
if (info->refCount != UINT_MAX) {
|
||||
info->refCount++;
|
||||
registerExternalModuleAliases(requested, *resolvedPath, info);
|
||||
lastError = ERROR_SUCCESS;
|
||||
}
|
||||
registerExternalModuleAliases(requested, files::canonicalPath(path), info);
|
||||
return info;
|
||||
}
|
||||
|
||||
FILE *file = fopen(resolvedPath->c_str(), "rb");
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
if (!file) {
|
||||
perror("loadModule");
|
||||
lastError = ERROR_MOD_NOT_FOUND;
|
||||
diskError = ERROR_MOD_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto executable = std::make_unique<Executable>();
|
||||
if (!executable->loadPE(file, true)) {
|
||||
DEBUG_LOG(" loadPE failed for %s\n", resolvedPath->c_str());
|
||||
DEBUG_LOG(" loadPE failed for %s\n", path.c_str());
|
||||
fclose(file);
|
||||
lastError = ERROR_BAD_EXE_FORMAT;
|
||||
diskError = ERROR_BAD_EXE_FORMAT;
|
||||
return nullptr;
|
||||
}
|
||||
fclose(file);
|
||||
@ -696,8 +663,8 @@ HMODULE loadModule(const char *dllName) {
|
||||
ModulePtr info = std::make_unique<ModuleInfo>();
|
||||
info->module = nullptr;
|
||||
info->originalName = requested;
|
||||
info->normalizedName = alias;
|
||||
info->resolvedPath = *resolvedPath;
|
||||
info->normalizedName = normalizedBaseKey(parsed);
|
||||
info->resolvedPath = files::canonicalPath(path);
|
||||
info->executable = std::move(executable);
|
||||
info->entryPoint = info->executable->entryPoint;
|
||||
info->imageBase = info->executable->imageBuffer;
|
||||
@ -708,13 +675,72 @@ HMODULE loadModule(const char *dllName) {
|
||||
|
||||
ModuleInfo *raw = info.get();
|
||||
reg.modulesByKey[key] = std::move(info);
|
||||
registerExternalModuleAliases(requested, *resolvedPath, raw);
|
||||
registerExternalModuleAliases(requested, raw->resolvedPath, raw);
|
||||
ensureExportsInitialized(*raw);
|
||||
|
||||
callDllMain(*raw, DLL_PROCESS_ATTACH);
|
||||
lastError = ERROR_SUCCESS;
|
||||
|
||||
return raw;
|
||||
};
|
||||
|
||||
auto resolveAndLoadExternal = [&]() -> ModuleInfo * {
|
||||
auto resolvedPath = resolveModuleOnDisk(requested, false);
|
||||
if (!resolvedPath) {
|
||||
DEBUG_LOG(" module not found on disk\n");
|
||||
return nullptr;
|
||||
}
|
||||
return tryLoadExternal(*resolvedPath);
|
||||
};
|
||||
|
||||
std::string alias = normalizedBaseKey(parsed);
|
||||
ModuleInfo *existing = findByAlias(alias);
|
||||
if (!existing) {
|
||||
existing = findByAlias(normalizeAlias(requested));
|
||||
}
|
||||
if (existing) {
|
||||
DEBUG_LOG(" found existing module alias %s (builtin=%d)\n", alias.c_str(), existing->module != nullptr);
|
||||
if (existing->module == nullptr) {
|
||||
if (existing->refCount != UINT_MAX) {
|
||||
existing->refCount++;
|
||||
}
|
||||
DEBUG_LOG(" returning existing external module %s\n", existing->originalName.c_str());
|
||||
lastError = ERROR_SUCCESS;
|
||||
return existing;
|
||||
}
|
||||
if (ModuleInfo *external = resolveAndLoadExternal()) {
|
||||
DEBUG_LOG(" replaced builtin module %s with external copy\n", requested.c_str());
|
||||
lastError = ERROR_SUCCESS;
|
||||
return external;
|
||||
}
|
||||
lastError = ERROR_SUCCESS;
|
||||
DEBUG_LOG(" returning builtin module %s\n", existing->originalName.c_str());
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (ModuleInfo *external = resolveAndLoadExternal()) {
|
||||
DEBUG_LOG(" loaded external module %s\n", requested.c_str());
|
||||
lastError = ERROR_SUCCESS;
|
||||
return external;
|
||||
}
|
||||
|
||||
auto fallbackAlias = normalizedBaseKey(parsed);
|
||||
ModuleInfo *builtin = nullptr;
|
||||
auto builtinIt = reg.builtinAliasMap.find(fallbackAlias);
|
||||
if (builtinIt != reg.builtinAliasMap.end()) {
|
||||
builtin = builtinIt->second;
|
||||
}
|
||||
if (!builtin) {
|
||||
builtinIt = reg.builtinAliasMap.find(normalizeAlias(requested));
|
||||
if (builtinIt != reg.builtinAliasMap.end()) {
|
||||
builtin = builtinIt->second;
|
||||
}
|
||||
}
|
||||
if (builtin && builtin->module != nullptr) {
|
||||
DEBUG_LOG(" falling back to builtin module %s\n", builtin->originalName.c_str());
|
||||
lastError = (diskError != ERROR_SUCCESS) ? diskError : ERROR_SUCCESS;
|
||||
return builtin;
|
||||
}
|
||||
|
||||
lastError = (diskError != ERROR_SUCCESS) ? diskError : ERROR_MOD_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void freeModule(HMODULE module) {
|
||||
|
243
processes.cpp
243
processes.cpp
@ -1,7 +1,21 @@
|
||||
#include "processes.h"
|
||||
#include "common.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <spawn.h>
|
||||
#include <strings.h>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
extern "C" char **environ;
|
||||
|
||||
namespace processes {
|
||||
void *allocProcessHandle(pid_t pid) {
|
||||
@ -21,4 +35,233 @@ namespace processes {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasDirectoryComponent(const std::string &command) {
|
||||
return command.find('/') != std::string::npos || command.find('\\') != std::string::npos ||
|
||||
command.find(':') != std::string::npos;
|
||||
}
|
||||
|
||||
static bool hasExtension(const std::string &command) {
|
||||
auto pos = command.find_last_of('.');
|
||||
auto slash = command.find_last_of("/\\");
|
||||
return pos != std::string::npos && (slash == std::string::npos || pos > slash + 1);
|
||||
}
|
||||
|
||||
static std::vector<std::string> pathextValues() {
|
||||
const char *envValue = std::getenv("PATHEXT");
|
||||
std::string raw = envValue ? envValue : ".COM;.EXE;.BAT;.CMD";
|
||||
std::vector<std::string> exts;
|
||||
size_t start = 0;
|
||||
while (start <= raw.size()) {
|
||||
size_t end = raw.find(';', start);
|
||||
if (end == std::string::npos) {
|
||||
end = raw.size();
|
||||
}
|
||||
std::string part = raw.substr(start, end - start);
|
||||
if (!part.empty()) {
|
||||
if (part[0] != '.') {
|
||||
part.insert(part.begin(), '.');
|
||||
}
|
||||
exts.push_back(part);
|
||||
}
|
||||
if (end == raw.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
if (exts.empty()) {
|
||||
exts = {".COM", ".EXE", ".BAT", ".CMD"};
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> parseHostPath(const std::string &value) {
|
||||
std::vector<std::filesystem::path> paths;
|
||||
const char *delims = strchr(value.c_str(), ';') ? ";" : ":";
|
||||
size_t start = 0;
|
||||
while (start <= value.size()) {
|
||||
size_t end = value.find_first_of(delims, start);
|
||||
if (end == std::string::npos) {
|
||||
end = value.size();
|
||||
}
|
||||
std::string entry = value.substr(start, end - start);
|
||||
if (!entry.empty()) {
|
||||
bool looksWindows = entry.find('\\') != std::string::npos ||
|
||||
(entry.size() >= 2 && entry[1] == ':' && entry[0] != '/');
|
||||
std::filesystem::path candidate;
|
||||
if (looksWindows) {
|
||||
auto converted = files::pathFromWindows(entry.c_str());
|
||||
if (!converted.empty()) {
|
||||
candidate = converted;
|
||||
}
|
||||
}
|
||||
if (candidate.empty()) {
|
||||
candidate = std::filesystem::path(entry);
|
||||
}
|
||||
paths.push_back(std::move(candidate));
|
||||
}
|
||||
if (end == value.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> buildSearchDirectories() {
|
||||
std::vector<std::filesystem::path> dirs;
|
||||
dirs.push_back(std::filesystem::current_path());
|
||||
if (const char *envPath = std::getenv("PATH")) {
|
||||
auto parsed = parseHostPath(envPath);
|
||||
dirs.insert(dirs.end(), parsed.begin(), parsed.end());
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath) {
|
||||
if (command.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> candidates;
|
||||
candidates.push_back(command);
|
||||
if (!hasExtension(command)) {
|
||||
for (const auto &ext : pathextValues()) {
|
||||
candidates.push_back(command + ext);
|
||||
}
|
||||
}
|
||||
|
||||
auto tryResolveDirect = [&](const std::string &name) -> std::optional<std::filesystem::path> {
|
||||
auto host = files::pathFromWindows(name.c_str());
|
||||
if (host.empty()) {
|
||||
std::string normalized = name;
|
||||
std::replace(normalized.begin(), normalized.end(), '\\', '/');
|
||||
host = std::filesystem::path(normalized);
|
||||
}
|
||||
std::filesystem::path parent = host.parent_path().empty() ? std::filesystem::current_path() : host.parent_path();
|
||||
std::string filename = host.filename().string();
|
||||
auto resolved = files::findCaseInsensitiveFile(parent, filename);
|
||||
if (resolved) {
|
||||
return files::canonicalPath(*resolved);
|
||||
}
|
||||
std::error_code ec;
|
||||
if (!filename.empty() && std::filesystem::exists(host, ec)) {
|
||||
return files::canonicalPath(host);
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
if (hasDirectoryComponent(command)) {
|
||||
for (const auto &name : candidates) {
|
||||
auto resolved = tryResolveDirect(name);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (searchPath) {
|
||||
auto dirs = buildSearchDirectories();
|
||||
for (const auto &dir : dirs) {
|
||||
for (const auto &name : candidates) {
|
||||
auto resolved = files::findCaseInsensitiveFile(dir, name);
|
||||
if (resolved) {
|
||||
return files::canonicalPath(*resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int spawnViaWibo(const std::filesystem::path &hostExecutable, const std::vector<std::string> &arguments, pid_t *pidOut) {
|
||||
if (hostExecutable.empty()) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
std::vector<std::string> storage;
|
||||
storage.reserve(arguments.size() + 1);
|
||||
storage.push_back(hostExecutable.string());
|
||||
for (const auto &arg : arguments) {
|
||||
storage.push_back(arg);
|
||||
}
|
||||
|
||||
std::vector<char *> nativeArgs;
|
||||
nativeArgs.reserve(storage.size() + 2);
|
||||
nativeArgs.push_back(wibo::executableName);
|
||||
for (auto &entry : storage) {
|
||||
nativeArgs.push_back(entry.data());
|
||||
}
|
||||
nativeArgs.push_back(nullptr);
|
||||
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawn_file_actions_init(&actions);
|
||||
|
||||
std::string indent = std::to_string(wibo::debugIndent + 1);
|
||||
setenv("WIBO_DEBUG_INDENT", indent.c_str(), 1);
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = posix_spawn(&pid, wibo::executableName, &actions, nullptr, nativeArgs.data(), environ);
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
if (spawnResult != 0) {
|
||||
return spawnResult;
|
||||
}
|
||||
if (pidOut) {
|
||||
*pidOut = pid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine) {
|
||||
std::vector<std::string> result;
|
||||
if (!commandLine) {
|
||||
return result;
|
||||
}
|
||||
std::string input(commandLine);
|
||||
size_t i = 0;
|
||||
size_t len = input.size();
|
||||
while (i < len) {
|
||||
while (i < len && (input[i] == ' ' || input[i] == '\t')) {
|
||||
++i;
|
||||
}
|
||||
if (i >= len) {
|
||||
break;
|
||||
}
|
||||
std::string arg;
|
||||
bool inQuotes = false;
|
||||
int backslashes = 0;
|
||||
for (; i < len; ++i) {
|
||||
char c = input[i];
|
||||
if (c == '\\') {
|
||||
++backslashes;
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
if ((backslashes % 2) == 0) {
|
||||
arg.append(backslashes / 2, '\\');
|
||||
inQuotes = !inQuotes;
|
||||
} else {
|
||||
arg.append(backslashes / 2, '\\');
|
||||
arg.push_back('"');
|
||||
}
|
||||
backslashes = 0;
|
||||
continue;
|
||||
}
|
||||
arg.append(backslashes, '\\');
|
||||
backslashes = 0;
|
||||
if (!inQuotes && (c == ' ' || c == '\t')) {
|
||||
break;
|
||||
}
|
||||
arg.push_back(c);
|
||||
}
|
||||
arg.append(backslashes, '\\');
|
||||
result.push_back(std::move(arg));
|
||||
while (i < len && (input[i] == ' ' || input[i] == '\t')) {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <sched.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace processes {
|
||||
struct Process {
|
||||
@ -11,4 +15,8 @@ namespace processes {
|
||||
|
||||
void *allocProcessHandle(pid_t pid);
|
||||
Process* processFromHandle(void* hHandle, bool pop);
|
||||
|
||||
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath);
|
||||
int spawnViaWibo(const std::filesystem::path &hostExecutable, const std::vector<std::string> &arguments, pid_t *pidOut);
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user