mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
New handles, threading and processes subsystems
This commit is contained in:
parent
704dfd90ec
commit
390f26b28d
@ -73,6 +73,7 @@ add_executable(wibo
|
||||
dll/user32.cpp
|
||||
dll/vcruntime.cpp
|
||||
dll/version.cpp
|
||||
access.cpp
|
||||
errors.cpp
|
||||
files.cpp
|
||||
handles.cpp
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "access_mask.h"
|
||||
#include "access.h"
|
||||
|
||||
namespace wibo::access {
|
||||
|
||||
@ -42,9 +42,4 @@ NormalizedAccess normalizeDesiredAccess(uint32_t desiredMask, const GenericMappi
|
||||
return out;
|
||||
}
|
||||
|
||||
// handles.cpp: normalizeDesiredAccess(...) can feed HandleTable::create so duplicates never exceed original rights.
|
||||
// files.cpp: store the NormalizedAccess::grantedMask in FileObject::maxAccessMask (or similar) for later enforcement.
|
||||
// dll/kernel32/fileapi.cpp: replace meta.grantedAccess & GENERIC_* checks with FileAccessView helpers once rights are
|
||||
// canonical.
|
||||
|
||||
} // namespace wibo::access
|
140
common.h
140
common.h
@ -78,6 +78,79 @@ using LPWSTR = uint16_t *;
|
||||
using LPCWSTR = const uint16_t *;
|
||||
using LPCWCH = const uint16_t *;
|
||||
using WCHAR = uint16_t;
|
||||
using LPCH = char *;
|
||||
using LPWCH = uint16_t *;
|
||||
using BOOL = int;
|
||||
using PBOOL = BOOL *;
|
||||
using LPBOOL = BOOL *;
|
||||
using UCHAR = unsigned char;
|
||||
using PUCHAR = UCHAR *;
|
||||
using SIZE_T = size_t;
|
||||
using PSIZE_T = SIZE_T *;
|
||||
using BYTE = unsigned char;
|
||||
using BOOLEAN = unsigned char;
|
||||
using UINT = unsigned int;
|
||||
using HKEY = void *;
|
||||
using PHKEY = HKEY *;
|
||||
using PSID = void *;
|
||||
using REGSAM = DWORD;
|
||||
using LSTATUS = LONG;
|
||||
using LCID = DWORD;
|
||||
using LCTYPE = DWORD;
|
||||
|
||||
constexpr BOOL TRUE = 1;
|
||||
constexpr BOOL FALSE = 0;
|
||||
|
||||
constexpr DWORD STILL_ACTIVE = 259;
|
||||
|
||||
constexpr DWORD FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||
constexpr DWORD FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
|
||||
constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||
constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||
|
||||
constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
|
||||
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
|
||||
constexpr DWORD STD_ERROR_HANDLE = ((DWORD)-12);
|
||||
|
||||
constexpr DWORD FILE_READ_DATA = 0x00000001;
|
||||
constexpr DWORD FILE_LIST_DIRECTORY = 0x00000001;
|
||||
constexpr DWORD FILE_WRITE_DATA = 0x00000002;
|
||||
constexpr DWORD FILE_ADD_FILE = 0x00000002;
|
||||
constexpr DWORD FILE_APPEND_DATA = 0x00000004;
|
||||
constexpr DWORD FILE_ADD_SUBDIRECTORY = 0x00000004;
|
||||
constexpr DWORD FILE_CREATE_PIPE_INSTANCE = 0x00000004;
|
||||
constexpr DWORD FILE_READ_EA = 0x00000008;
|
||||
constexpr DWORD FILE_WRITE_EA = 0x00000010;
|
||||
constexpr DWORD FILE_EXECUTE = 0x00000020;
|
||||
constexpr DWORD FILE_TRAVERSE = 0x00000020;
|
||||
constexpr DWORD FILE_DELETE_CHILD = 0x00000040;
|
||||
constexpr DWORD FILE_READ_ATTRIBUTES = 0x00000080;
|
||||
constexpr DWORD FILE_WRITE_ATTRIBUTES = 0x00000100;
|
||||
|
||||
constexpr DWORD SYNCHRONIZE = 0x00100000;
|
||||
constexpr DWORD DELETE = 0x00010000;
|
||||
|
||||
constexpr DWORD STANDARD_RIGHTS_READ = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_WRITE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_EXECUTE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_REQUIRED = 0x000f0000;
|
||||
constexpr DWORD STANDARD_RIGHTS_ALL = 0x001f0000;
|
||||
|
||||
constexpr DWORD FILE_GENERIC_READ =
|
||||
STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_WRITE =
|
||||
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF;
|
||||
|
||||
constexpr DWORD EVENT_ALL_ACCESS = 0x1F0003;
|
||||
constexpr DWORD MUTEX_ALL_ACCESS = 0x1F0001;
|
||||
constexpr DWORD SEMAPHORE_ALL_ACCESS = 0x1F0003;
|
||||
|
||||
constexpr DWORD GENERIC_READ = 0x80000000;
|
||||
constexpr DWORD GENERIC_WRITE = 0x40000000;
|
||||
constexpr DWORD GENERIC_EXECUTE = 0x20000000;
|
||||
constexpr DWORD GENERIC_ALL = 0x10000000;
|
||||
|
||||
// Page protection constants
|
||||
constexpr DWORD PAGE_NOACCESS = 0x01;
|
||||
@ -116,70 +189,11 @@ constexpr DWORD FILE_MAP_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_MAP_READ = 0x00000004;
|
||||
constexpr DWORD FILE_MAP_EXECUTE = 0x00000020;
|
||||
constexpr DWORD FILE_MAP_ALL_ACCESS = 0x000f001f;
|
||||
using LPCH = char *;
|
||||
using LPWCH = uint16_t *;
|
||||
using BOOL = int;
|
||||
using PBOOL = BOOL *;
|
||||
using LPBOOL = BOOL *;
|
||||
using UCHAR = unsigned char;
|
||||
using PUCHAR = UCHAR *;
|
||||
using SIZE_T = size_t;
|
||||
using PSIZE_T = SIZE_T *;
|
||||
using BYTE = unsigned char;
|
||||
using BOOLEAN = unsigned char;
|
||||
using UINT = unsigned int;
|
||||
using HKEY = void *;
|
||||
using PHKEY = HKEY *;
|
||||
using PSID = void *;
|
||||
using REGSAM = DWORD;
|
||||
using LSTATUS = LONG;
|
||||
using LCID = DWORD;
|
||||
using LCTYPE = DWORD;
|
||||
|
||||
constexpr BOOL TRUE = 1;
|
||||
constexpr BOOL FALSE = 0;
|
||||
|
||||
constexpr DWORD STILL_ACTIVE = 259;
|
||||
|
||||
constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||
constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||
|
||||
constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10);
|
||||
constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11);
|
||||
constexpr DWORD STD_ERROR_HANDLE = ((DWORD)-12);
|
||||
|
||||
constexpr DWORD FILE_READ_DATA = 0x00000001;
|
||||
constexpr DWORD FILE_LIST_DIRECTORY = 0x00000001;
|
||||
constexpr DWORD FILE_WRITE_DATA = 0x00000002;
|
||||
constexpr DWORD FILE_ADD_FILE = 0x00000002;
|
||||
constexpr DWORD FILE_APPEND_DATA = 0x00000004;
|
||||
constexpr DWORD FILE_ADD_SUBDIRECTORY = 0x00000004;
|
||||
constexpr DWORD FILE_CREATE_PIPE_INSTANCE = 0x00000004;
|
||||
constexpr DWORD FILE_READ_EA = 0x00000008;
|
||||
constexpr DWORD FILE_WRITE_EA = 0x00000010;
|
||||
constexpr DWORD FILE_EXECUTE = 0x00000020;
|
||||
constexpr DWORD FILE_TRAVERSE = 0x00000020;
|
||||
constexpr DWORD FILE_DELETE_CHILD = 0x00000040;
|
||||
constexpr DWORD FILE_READ_ATTRIBUTES = 0x00000080;
|
||||
constexpr DWORD FILE_WRITE_ATTRIBUTES = 0x00000100;
|
||||
|
||||
constexpr DWORD STANDARD_RIGHTS_READ = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_WRITE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_EXECUTE = 0x00020000;
|
||||
constexpr DWORD STANDARD_RIGHTS_REQUIRED = 0x000f0000;
|
||||
constexpr DWORD SYNCHRONIZE = 0x00100000;
|
||||
|
||||
constexpr DWORD FILE_GENERIC_READ =
|
||||
STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_WRITE =
|
||||
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE;
|
||||
constexpr DWORD FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF;
|
||||
|
||||
constexpr DWORD GENERIC_READ = 0x80000000;
|
||||
constexpr DWORD GENERIC_WRITE = 0x40000000;
|
||||
constexpr DWORD GENERIC_EXECUTE = 0x20000000;
|
||||
constexpr DWORD GENERIC_ALL = 0x10000000;
|
||||
// File share modes
|
||||
constexpr DWORD FILE_SHARE_READ = 0x00000001;
|
||||
constexpr DWORD FILE_SHARE_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_SHARE_DELETE = 0x00000004;
|
||||
|
||||
struct UNICODE_STRING {
|
||||
unsigned short Length;
|
||||
|
@ -1,15 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "handles.h"
|
||||
#include "securitybaseapi.h"
|
||||
|
||||
constexpr DWORD SECURITY_LOCAL_SYSTEM_RID = 18;
|
||||
|
||||
constexpr BYTE kNtAuthority[6] = {0, 0, 0, 0, 0, 5};
|
||||
|
||||
struct TokenObject {
|
||||
HANDLE processHandle;
|
||||
struct TokenObject : ObjectBase {
|
||||
static constexpr ObjectType kType = ObjectType::Token;
|
||||
|
||||
Pin<> obj;
|
||||
DWORD desiredAccess;
|
||||
|
||||
explicit TokenObject(Pin<> obj, DWORD desiredAccess)
|
||||
: ObjectBase(kType), obj(std::move(obj)), desiredAccess(desiredAccess) {}
|
||||
};
|
||||
|
||||
using SidIdentifierAuthority = SID_IDENTIFIER_AUTHORITY;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "processes.h"
|
||||
|
||||
namespace advapi32 {
|
||||
|
||||
@ -12,14 +13,13 @@ BOOL WIN_FUNC OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDL
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
auto *token = new TokenObject;
|
||||
token->processHandle = ProcessHandle;
|
||||
token->desiredAccess = DesiredAccess;
|
||||
handles::Data data;
|
||||
data.type = handles::TYPE_TOKEN;
|
||||
data.ptr = token;
|
||||
data.size = sizeof(TokenObject);
|
||||
*TokenHandle = handles::allocDataHandle(data);
|
||||
auto obj = wibo::handles().getAs<ProcessObject>(ProcessHandle);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
auto token = make_pin<TokenObject>(std::move(obj), DesiredAccess);
|
||||
*TokenHandle = wibo::handles().alloc(std::move(token), 0, 0);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -323,21 +323,14 @@ BOOL WIN_FUNC DuplicateTokenEx(HANDLE hExistingToken, DWORD dwDesiredAccess, voi
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
handles::Data existingData = handles::dataFromHandle(hExistingToken, false);
|
||||
if (existingData.type != handles::TYPE_TOKEN || existingData.ptr == nullptr) {
|
||||
auto existing = wibo::handles().getAs<TokenObject>(hExistingToken);
|
||||
if (!existing) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
auto *existingToken = static_cast<TokenObject *>(existingData.ptr);
|
||||
auto *newToken = new TokenObject(*existingToken);
|
||||
if (dwDesiredAccess != 0) {
|
||||
newToken->desiredAccess = dwDesiredAccess;
|
||||
}
|
||||
handles::Data newData;
|
||||
newData.type = handles::TYPE_TOKEN;
|
||||
newData.ptr = newToken;
|
||||
newData.size = sizeof(TokenObject);
|
||||
*phNewToken = handles::allocDataHandle(newData);
|
||||
auto newToken =
|
||||
make_pin<TokenObject>(existing->obj.clone(), dwDesiredAccess == 0 ? existing->desiredAccess : dwDesiredAccess);
|
||||
*phNewToken = wibo::handles().alloc(std::move(newToken), 0, 0);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -411,8 +404,8 @@ BOOL WIN_FUNC SetKernelObjectSecurity(HANDLE Handle, SECURITY_INFORMATION Securi
|
||||
wibo::lastError = ERROR_INVALID_SECURITY_DESCR;
|
||||
return FALSE;
|
||||
}
|
||||
auto data = handles::dataFromHandle(Handle, false);
|
||||
if (data.type == handles::TYPE_UNUSED) {
|
||||
auto obj = wibo::handles().get(Handle);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -461,14 +454,14 @@ BOOL WIN_FUNC SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor
|
||||
|
||||
BOOL WIN_FUNC GetTokenInformation(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
|
||||
LPVOID TokenInformation, DWORD TokenInformationLength, LPDWORD ReturnLength) {
|
||||
DEBUG_LOG("GetTokenInformation(%p, %u, %p, %u, %p)\n", TokenHandle, TokenInformationClass, TokenInformation,
|
||||
DEBUG_LOG("STUB: GetTokenInformation(%p, %u, %p, %u, %p)\n", TokenHandle, TokenInformationClass, TokenInformation,
|
||||
TokenInformationLength, ReturnLength);
|
||||
if (!ReturnLength) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
auto data = handles::dataFromHandle(TokenHandle, false);
|
||||
if (data.type != handles::TYPE_TOKEN) {
|
||||
auto token = wibo::handles().getAs<TokenObject>(TokenHandle);
|
||||
if (!token) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -563,8 +556,8 @@ BOOL WIN_FUNC SetTokenInformation(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS To
|
||||
(void)TokenInformationClass;
|
||||
(void)TokenInformation;
|
||||
(void)TokenInformationLength;
|
||||
auto data = handles::dataFromHandle(TokenHandle, false);
|
||||
if (data.type != handles::TYPE_TOKEN) {
|
||||
auto token = wibo::handles().getAs<TokenObject>(TokenHandle);
|
||||
if (!token) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -12,9 +12,16 @@
|
||||
|
||||
namespace {
|
||||
|
||||
struct RegistryKeyHandleData {
|
||||
struct RegistryKeyObject : ObjectBase {
|
||||
static constexpr ObjectType kType = ObjectType::RegistryKey;
|
||||
|
||||
std::mutex m;
|
||||
std::u16string canonicalPath;
|
||||
bool closed = false;
|
||||
bool predefined = false;
|
||||
|
||||
RegistryKeyObject() : ObjectBase(kType) {}
|
||||
explicit RegistryKeyObject(std::u16string path) : ObjectBase(kType), canonicalPath(std::move(path)) {}
|
||||
};
|
||||
|
||||
struct PredefinedKeyInfo {
|
||||
@ -34,9 +41,6 @@ constexpr PredefinedKeyInfo kPredefinedKeyInfos[] = {
|
||||
constexpr size_t kPredefinedKeyCount = std::size(kPredefinedKeyInfos);
|
||||
|
||||
std::mutex g_registryMutex;
|
||||
bool g_predefinedHandlesInitialized = false;
|
||||
RegistryKeyHandleData g_predefinedHandles[kPredefinedKeyCount];
|
||||
bool g_registryInitialized = false;
|
||||
std::unordered_set<std::u16string> g_existingKeys;
|
||||
|
||||
std::u16string canonicalizeKeySegment(const std::u16string &input) {
|
||||
@ -74,63 +78,49 @@ std::u16string canonicalizeKeySegment(LPCWSTR input) {
|
||||
return canonicalizeKeySegment(wide);
|
||||
}
|
||||
|
||||
void initializePredefinedHandlesLocked() {
|
||||
if (g_predefinedHandlesInitialized) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < kPredefinedKeyCount; ++i) {
|
||||
g_predefinedHandles[i].canonicalPath = canonicalizeKeySegment(std::u16string(kPredefinedKeyInfos[i].name));
|
||||
g_predefinedHandles[i].predefined = true;
|
||||
}
|
||||
g_predefinedHandlesInitialized = true;
|
||||
}
|
||||
|
||||
RegistryKeyHandleData *predefinedHandleForValue(uintptr_t value) {
|
||||
Pin<RegistryKeyObject> predefinedHandleForValue(uintptr_t value) {
|
||||
static std::array<Pin<RegistryKeyObject>, kPredefinedKeyCount> g_predefinedHandles = [] {
|
||||
std::array<Pin<RegistryKeyObject>, kPredefinedKeyCount> arr;
|
||||
for (size_t i = 0; i < kPredefinedKeyCount; ++i) {
|
||||
arr[i] = make_pin<RegistryKeyObject>();
|
||||
arr[i]->canonicalPath = canonicalizeKeySegment(std::u16string(kPredefinedKeyInfos[i].name));
|
||||
arr[i]->predefined = true;
|
||||
}
|
||||
return arr;
|
||||
}();
|
||||
for (size_t i = 0; i < kPredefinedKeyCount; ++i) {
|
||||
if (kPredefinedKeyInfos[i].value == value) {
|
||||
return &g_predefinedHandles[i];
|
||||
return g_predefinedHandles[i].clone();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
RegistryKeyHandleData *handleDataFromHKeyLocked(HKEY hKey) {
|
||||
Pin<RegistryKeyObject> handleDataFromHKeyLocked(HKEY hKey) {
|
||||
uintptr_t raw = reinterpret_cast<uintptr_t>(hKey);
|
||||
if (raw == 0) {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
initializePredefinedHandlesLocked();
|
||||
if (auto *predefined = predefinedHandleForValue(raw)) {
|
||||
if (auto predefined = predefinedHandleForValue(raw)) {
|
||||
return predefined;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hKey, false);
|
||||
if (data.type != handles::TYPE_REGISTRY_KEY || data.ptr == nullptr) {
|
||||
return nullptr;
|
||||
auto obj = wibo::handles().getAs<RegistryKeyObject>(hKey);
|
||||
if (!obj || obj->closed) {
|
||||
return {};
|
||||
}
|
||||
return static_cast<RegistryKeyHandleData *>(data.ptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool isPredefinedKeyHandle(HKEY hKey) {
|
||||
uintptr_t raw = reinterpret_cast<uintptr_t>(hKey);
|
||||
for (size_t i = 0; i < kPredefinedKeyCount; ++i) {
|
||||
if (kPredefinedKeyInfos[i].value == raw) {
|
||||
for (const auto &kPredefinedKeyInfo : kPredefinedKeyInfos) {
|
||||
if (kPredefinedKeyInfo.value == raw) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ensureRegistryInitializedLocked() {
|
||||
if (g_registryInitialized) {
|
||||
return;
|
||||
}
|
||||
initializePredefinedHandlesLocked();
|
||||
for (auto &g_predefinedHandle : g_predefinedHandles) {
|
||||
g_existingKeys.insert(g_predefinedHandle.canonicalPath);
|
||||
}
|
||||
g_registryInitialized = true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace advapi32 {
|
||||
@ -163,8 +153,7 @@ LSTATUS WIN_FUNC RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LP
|
||||
DEBUG_LOG("RegCreateKeyExW: ignoring WOW64 access mask 0x%x\n", samDesired ^ sanitizedAccess);
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(g_registryMutex);
|
||||
ensureRegistryInitializedLocked();
|
||||
RegistryKeyHandleData *baseHandle = handleDataFromHKeyLocked(hKey);
|
||||
Pin<RegistryKeyObject> baseHandle = handleDataFromHKeyLocked(hKey);
|
||||
if (!baseHandle) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return ERROR_INVALID_HANDLE;
|
||||
@ -197,10 +186,8 @@ LSTATUS WIN_FUNC RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LP
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
auto *handleData = new RegistryKeyHandleData;
|
||||
handleData->canonicalPath = targetPath;
|
||||
handleData->predefined = false;
|
||||
auto handle = handles::allocDataHandle({handles::TYPE_REGISTRY_KEY, handleData, sizeof(*handleData)});
|
||||
auto obj = make_pin<RegistryKeyObject>(std::move(targetPath));
|
||||
auto handle = wibo::handles().alloc(std::move(obj), 0, 0);
|
||||
*phkResult = reinterpret_cast<HKEY>(handle);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return ERROR_SUCCESS;
|
||||
@ -246,8 +233,7 @@ LSTATUS WIN_FUNC RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REG
|
||||
}
|
||||
(void)sanitizedAccess;
|
||||
std::lock_guard<std::mutex> lock(g_registryMutex);
|
||||
ensureRegistryInitializedLocked();
|
||||
RegistryKeyHandleData *baseHandle = handleDataFromHKeyLocked(hKey);
|
||||
Pin<RegistryKeyObject> baseHandle = handleDataFromHKeyLocked(hKey);
|
||||
if (!baseHandle) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return ERROR_INVALID_HANDLE;
|
||||
@ -277,10 +263,8 @@ LSTATUS WIN_FUNC RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REG
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
auto *handleData = new RegistryKeyHandleData;
|
||||
handleData->canonicalPath = targetPath;
|
||||
handleData->predefined = false;
|
||||
auto handle = handles::allocDataHandle({handles::TYPE_REGISTRY_KEY, handleData, sizeof(*handleData)});
|
||||
auto obj = make_pin<RegistryKeyObject>(std::move(targetPath));
|
||||
auto handle = wibo::handles().alloc(std::move(obj), 0, 0);
|
||||
*phkResult = reinterpret_cast<HKEY>(handle);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return ERROR_SUCCESS;
|
||||
@ -391,13 +375,11 @@ LSTATUS WIN_FUNC RegCloseKey(HKEY hKey) {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hKey, true);
|
||||
if (data.type != handles::TYPE_REGISTRY_KEY || data.ptr == nullptr) {
|
||||
auto obj = wibo::handles().getAs<RegistryKeyObject>(hKey);
|
||||
if (!obj || obj->closed) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return ERROR_INVALID_HANDLE;
|
||||
}
|
||||
auto *handleData = static_cast<RegistryKeyHandleData *>(data.ptr);
|
||||
delete handleData;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "fileapi.h"
|
||||
|
||||
#include "access_mask.h"
|
||||
#include "access.h"
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
@ -41,6 +41,14 @@ const FILETIME kDefaultFindFileTime = {
|
||||
const FILETIME kDefaultFileInformationTime = {static_cast<DWORD>(UNIX_TIME_ZERO & 0xFFFFFFFFULL),
|
||||
static_cast<DWORD>(UNIX_TIME_ZERO >> 32)};
|
||||
|
||||
using wibo::access::containsAny;
|
||||
|
||||
constexpr uint32_t kFileReadMask = FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES;
|
||||
constexpr uint32_t kDirectoryReadMask = FILE_LIST_DIRECTORY | FILE_TRAVERSE | FILE_READ_EA | FILE_READ_ATTRIBUTES;
|
||||
constexpr uint32_t kFileWriteMask = FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
|
||||
constexpr uint32_t kDirectoryWriteMask =
|
||||
FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
|
||||
|
||||
struct timespec accessTimespec(const struct stat &st) {
|
||||
#if defined(__APPLE__)
|
||||
return st.st_atimespec;
|
||||
@ -252,7 +260,7 @@ bool tryOpenConsoleDevice(DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCrea
|
||||
return false;
|
||||
}
|
||||
HANDLE baseHandle = files::getStdHandle(*stdHandleKind);
|
||||
if (!wibo::handles().duplicateTo(baseHandle, wibo::handles(), &outHandle, dwDesiredAccess, false, 0)) {
|
||||
if (!wibo::handles().duplicateTo(baseHandle, wibo::handles(), outHandle, dwDesiredAccess, false, 0)) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
outHandle = INVALID_HANDLE_VALUE;
|
||||
return true;
|
||||
@ -265,21 +273,26 @@ bool tryOpenConsoleDevice(DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCrea
|
||||
|
||||
namespace kernel32 {
|
||||
|
||||
int64_t getFileSizeFromHandle(HANDLE hFile) {
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return -1;
|
||||
namespace {
|
||||
|
||||
void signalOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (ov && ov->hEvent) {
|
||||
if (auto ev = wibo::handles().getAs<EventObject>(ov->hEvent)) {
|
||||
ev->set();
|
||||
}
|
||||
}
|
||||
struct stat64 st{};
|
||||
fflush(fp);
|
||||
if (fstat64(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode)) {
|
||||
setLastErrorFromErrno();
|
||||
return -1;
|
||||
}
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
void resetOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (ov && ov->hEvent) {
|
||||
if (auto ev = wibo::handles().getAs<EventObject>(ov->hEvent)) {
|
||||
ev->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DWORD WIN_FUNC GetFileAttributesA(LPCSTR lpFileName) {
|
||||
if (!lpFileName) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
@ -424,20 +437,23 @@ LONG WIN_FUNC CompareFileTime(const FILETIME *lpFileTime1, const FILETIME *lpFil
|
||||
|
||||
BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten,
|
||||
LPOVERLAPPED lpOverlapped) {
|
||||
DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite);
|
||||
DEBUG_LOG("WriteFile(%p, %p, %u, %p, %p)\n", hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten,
|
||||
lpOverlapped);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if ((meta.grantedAccess & FILE_WRITE_DATA) == 0) {
|
||||
#ifdef CHECK_ACCESS
|
||||
if ((meta.grantedAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("!!! DENIED: 0x%x\n", meta.grantedAccess);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lpOverlapped == nullptr && lpNumberOfBytesWritten == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
@ -460,15 +476,8 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||
if (lpNumberOfBytesWritten) {
|
||||
*lpNumberOfBytesWritten = io.bytesTransferred;
|
||||
}
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
return FALSE;
|
||||
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||
completionStatus = STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesWritten && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||
@ -481,17 +490,17 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return io.unixError == 0;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) {
|
||||
DEBUG_LOG("FlushFileBuffers(%p)\n", hFile);
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if (fsync(file->host_fd) != 0) {
|
||||
if (fsync(file->fd) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -505,16 +514,18 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
#ifdef CHECK_ACCESS
|
||||
if ((meta.grantedAccess & FILE_READ_DATA) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("!!! DENIED: 0x%x\n", meta.grantedAccess);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lpOverlapped == nullptr && lpNumberOfBytesRead == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
@ -537,15 +548,8 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||
if (lpNumberOfBytesRead) {
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
return FALSE;
|
||||
} else if (io.reachedEnd && io.bytesTransferred == 0) {
|
||||
completionStatus = STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
|
||||
@ -558,19 +562,18 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return io.unixError == 0;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
|
||||
(void)dwShareMode;
|
||||
(void)lpSecurityAttributes;
|
||||
(void)hTemplateFile;
|
||||
if (!lpFileName) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
HANDLE consoleHandle = INVALID_HANDLE_VALUE;
|
||||
if (tryOpenConsoleDevice(dwDesiredAccess, dwShareMode, dwCreationDisposition, dwFlagsAndAttributes, consoleHandle,
|
||||
std::string(lpFileName))) {
|
||||
@ -578,77 +581,209 @@ HANDLE WIN_FUNC CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSh
|
||||
dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, consoleHandle);
|
||||
return consoleHandle;
|
||||
}
|
||||
std::string path = files::pathFromWindows(lpFileName);
|
||||
|
||||
std::filesystem::path hostPath = files::pathFromWindows(lpFileName);
|
||||
std::string hostPathStr = hostPath.string();
|
||||
DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, "
|
||||
"creationDisposition=%u, flagsAndAttributes=%u)\n",
|
||||
lpFileName, path.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes);
|
||||
lpFileName, hostPathStr.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes,
|
||||
dwCreationDisposition, dwFlagsAndAttributes);
|
||||
|
||||
constexpr DWORD kAttributeMask = 0x0000FFFFu;
|
||||
DWORD fileAttributes = dwFlagsAndAttributes & kAttributeMask;
|
||||
bool backupSemantics = (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) != 0;
|
||||
bool deleteOnClose = (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE) != 0;
|
||||
bool overlapped = (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) != 0;
|
||||
|
||||
std::error_code statusEc;
|
||||
std::filesystem::file_status status = std::filesystem::status(hostPath, statusEc);
|
||||
bool pathExists = !statusEc && status.type() != std::filesystem::file_type::not_found;
|
||||
bool isDirectory = pathExists && status.type() == std::filesystem::file_type::directory;
|
||||
|
||||
if ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && !isDirectory) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
DEBUG_LOG("-> ERROR_INVALID_PARAMETER (ENOTDIR)\n");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (isDirectory && (!backupSemantics || deleteOnClose /* not currently implemented for dir */)) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED (EISDIR)\n");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// TODO: verify share mode against existing opens
|
||||
|
||||
bool allowCreate = false;
|
||||
bool truncateExisting = false;
|
||||
bool existedBefore = pathExists;
|
||||
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
bool fileExists = (access(path.c_str(), F_OK) == 0);
|
||||
bool shouldTruncate = false;
|
||||
switch (dwCreationDisposition) {
|
||||
case CREATE_ALWAYS:
|
||||
if (fileExists) {
|
||||
wibo::lastError = ERROR_ALREADY_EXISTS;
|
||||
shouldTruncate = true;
|
||||
}
|
||||
break;
|
||||
case CREATE_NEW:
|
||||
if (fileExists) {
|
||||
allowCreate = true;
|
||||
if (pathExists) {
|
||||
wibo::lastError = ERROR_FILE_EXISTS;
|
||||
DEBUG_LOG("-> ERROR_FILE_EXISTS (EEXIST)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
break;
|
||||
case CREATE_ALWAYS:
|
||||
allowCreate = true;
|
||||
if (isDirectory) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED (EISDIR)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
truncateExisting = pathExists;
|
||||
break;
|
||||
case OPEN_ALWAYS:
|
||||
if (fileExists) {
|
||||
wibo::lastError = ERROR_ALREADY_EXISTS;
|
||||
if (!pathExists) {
|
||||
allowCreate = true;
|
||||
} else if (isDirectory) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED (EISDIR)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
break;
|
||||
case OPEN_EXISTING:
|
||||
if (!fileExists) {
|
||||
if (!pathExists) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
DEBUG_LOG("-> ERROR_FILE_NOT_FOUND (ENOENT)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
break;
|
||||
case TRUNCATE_EXISTING:
|
||||
shouldTruncate = true;
|
||||
if (!fileExists) {
|
||||
if (!pathExists) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
DEBUG_LOG("-> ERROR_FILE_NOT_FOUND (ENOENT)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (isDirectory) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED (EISDIR)");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
truncateExisting = true;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
if (dwDesiredAccess == GENERIC_READ) {
|
||||
fd = open(path.c_str(), O_RDONLY);
|
||||
} else if (dwDesiredAccess == GENERIC_WRITE) {
|
||||
if (shouldTruncate || !fileExists) {
|
||||
fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
} else {
|
||||
fd = open(path.c_str(), O_RDWR);
|
||||
}
|
||||
} else if (dwDesiredAccess == (GENERIC_READ | GENERIC_WRITE)) {
|
||||
if (shouldTruncate || !fileExists) {
|
||||
fd = open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
} else {
|
||||
fd = open(path.c_str(), O_RDWR);
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
const auto &genericMapping =
|
||||
isDirectory ? wibo::access::kDirectoryGenericMapping : wibo::access::kFileGenericMapping;
|
||||
uint32_t supportedMask = isDirectory ? (FILE_ALL_ACCESS | wibo::access::kDirectorySpecificRightsMask)
|
||||
: (FILE_ALL_ACCESS | wibo::access::kFileSpecificRightsMask);
|
||||
uint32_t defaultMask = FILE_READ_ATTRIBUTES;
|
||||
auto normalized =
|
||||
wibo::access::normalizeDesiredAccess(dwDesiredAccess, genericMapping, supportedMask, SYNCHRONIZE, defaultMask);
|
||||
if (normalized.deniedMask != 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED: denied mask 0x%x\n", normalized.deniedMask);
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
bool overlapped = (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) != 0;
|
||||
auto handle = wibo::handles().create(new files::FileObject(fd, overlapped), dwDesiredAccess, 0);
|
||||
DEBUG_LOG("-> %p\n", handle);
|
||||
return handle;
|
||||
bool wantsRead = containsAny(normalized.grantedMask, isDirectory ? kDirectoryReadMask : kFileReadMask) ||
|
||||
containsAny(normalized.grantedMask, FILE_EXECUTE);
|
||||
bool wantsWrite = containsAny(normalized.grantedMask, isDirectory ? kDirectoryWriteMask : kFileWriteMask);
|
||||
bool appendRequested = !isDirectory && containsAny(normalized.grantedMask, FILE_APPEND_DATA);
|
||||
bool appendOnly = appendRequested && !containsAny(normalized.grantedMask, FILE_WRITE_DATA);
|
||||
#ifdef CHECK_ACCESS
|
||||
if (allowCreate && !containsAny(normalized.grantedMask, FILE_WRITE_DATA | FILE_APPEND_DATA)) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED: FILE_WRITE_DATA | FILE_APPEND_DATA required for creation");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
setLastErrorFromErrno();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
if (truncateExisting && !containsAny(normalized.grantedMask, FILE_WRITE_DATA)) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED: FILE_WRITE_DATA required for truncation");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (deleteOnClose && !containsAny(normalized.grantedMask, DELETE)) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("-> ERROR_ACCESS_DENIED: DELETE required for delete-on-close");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
(void)allowCreate;
|
||||
#endif
|
||||
|
||||
uint32_t shareMask = dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
|
||||
|
||||
int openFlags = O_CLOEXEC;
|
||||
mode_t createMode = 0666;
|
||||
bool requestCreate = false;
|
||||
if (dwCreationDisposition == CREATE_NEW || dwCreationDisposition == CREATE_ALWAYS ||
|
||||
(dwCreationDisposition == OPEN_ALWAYS && !pathExists)) {
|
||||
requestCreate = true;
|
||||
openFlags |= O_CREAT;
|
||||
}
|
||||
if (dwCreationDisposition == CREATE_NEW) {
|
||||
openFlags |= O_EXCL;
|
||||
}
|
||||
if (truncateExisting && !isDirectory) {
|
||||
openFlags |= O_TRUNC;
|
||||
}
|
||||
|
||||
if (isDirectory) {
|
||||
openFlags |= O_RDONLY | O_DIRECTORY;
|
||||
wantsRead = true;
|
||||
} else {
|
||||
bool needWrite = wantsWrite || truncateExisting || requestCreate;
|
||||
bool needRead = wantsRead || !needWrite;
|
||||
if (needWrite && needRead) {
|
||||
openFlags |= O_RDWR;
|
||||
} else if (needWrite) {
|
||||
openFlags |= O_WRONLY;
|
||||
} else {
|
||||
openFlags |= O_RDONLY;
|
||||
}
|
||||
if (appendOnly) {
|
||||
openFlags |= O_APPEND;
|
||||
}
|
||||
}
|
||||
|
||||
int fd = open(hostPathStr.c_str(), openFlags, createMode);
|
||||
if (fd < 0) {
|
||||
setLastErrorFromErrno();
|
||||
DEBUG_LOG("-> errno: %d\n", errno);
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
struct stat st{};
|
||||
if (fstat(fd, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
isDirectory = true;
|
||||
}
|
||||
|
||||
bool createdNew = !existedBefore && requestCreate;
|
||||
std::filesystem::path canonicalPath = files::canonicalPath(hostPath);
|
||||
|
||||
Pin<FsObject> fsObject;
|
||||
if (isDirectory) {
|
||||
fsObject = make_pin<DirectoryObject>(fd);
|
||||
} else {
|
||||
auto fileObj = make_pin<FileObject>(fd);
|
||||
fileObj->overlapped = overlapped;
|
||||
fileObj->appendOnly = appendOnly;
|
||||
fsObject = std::move(fileObj);
|
||||
}
|
||||
fsObject->canonicalPath = std::move(canonicalPath);
|
||||
fsObject->shareAccess = shareMask;
|
||||
fsObject->deletePending = deleteOnClose;
|
||||
|
||||
uint32_t handleFlags = 0;
|
||||
if (lpSecurityAttributes && lpSecurityAttributes->bInheritHandle) {
|
||||
handleFlags |= HANDLE_FLAG_INHERIT;
|
||||
}
|
||||
HANDLE handle = wibo::handles().alloc(std::move(fsObject), normalized.grantedMask, handleFlags);
|
||||
|
||||
if ((dwCreationDisposition == OPEN_ALWAYS && existedBefore) ||
|
||||
(dwCreationDisposition == CREATE_ALWAYS && existedBefore)) {
|
||||
wibo::lastError = ERROR_ALREADY_EXISTS;
|
||||
} else {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
DEBUG_LOG("-> %p (createdNew=%d, truncate=%d)\n", handle, createdNew ? 1 : 0, truncateExisting ? 1 : 0);
|
||||
return handle;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
||||
@ -737,19 +872,24 @@ DWORD WIN_FUNC SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistan
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
int origin = SEEK_SET;
|
||||
if (dwMoveMethod == FILE_CURRENT) {
|
||||
origin = SEEK_CUR;
|
||||
// TODO access check
|
||||
std::lock_guard lk(file->m);
|
||||
off64_t position = 0;
|
||||
off64_t offset = static_cast<off64_t>(lDistanceToMove);
|
||||
if (dwMoveMethod == FILE_BEGIN) {
|
||||
position = offset;
|
||||
} else if (dwMoveMethod == FILE_CURRENT) {
|
||||
position = file->filePos + offset;
|
||||
} else if (dwMoveMethod == FILE_END) {
|
||||
origin = SEEK_END;
|
||||
position = lseek64(file->fd, offset, SEEK_END);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
if (fseek(fp, lDistanceToMove, origin) < 0) {
|
||||
if (position < 0) {
|
||||
if (errno == EINVAL) {
|
||||
wibo::lastError = ERROR_NEGATIVE_SEEK;
|
||||
} else {
|
||||
@ -757,14 +897,11 @@ DWORD WIN_FUNC SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistan
|
||||
}
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
long position = ftell(fp);
|
||||
if (position < 0) {
|
||||
setLastErrorFromErrno();
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
file->filePos = position;
|
||||
if (lpDistanceToMoveHigh) {
|
||||
*lpDistanceToMoveHigh = static_cast<LONG>(static_cast<uint64_t>(position) >> 32);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return static_cast<DWORD>(static_cast<uint64_t>(position) & 0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
@ -774,29 +911,38 @@ BOOL WIN_FUNC SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLA
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
int origin = SEEK_SET;
|
||||
if (dwMoveMethod == FILE_CURRENT) {
|
||||
origin = SEEK_CUR;
|
||||
// TODO access check
|
||||
std::lock_guard lk(file->m);
|
||||
off64_t position = 0;
|
||||
off64_t offset = static_cast<off64_t>(liDistanceToMove);
|
||||
if (dwMoveMethod == FILE_BEGIN) {
|
||||
position = offset;
|
||||
} else if (dwMoveMethod == FILE_CURRENT) {
|
||||
position = file->filePos + offset;
|
||||
} else if (dwMoveMethod == FILE_END) {
|
||||
origin = SEEK_END;
|
||||
position = lseek64(file->fd, offset, SEEK_END);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
if (fseeko(fp, static_cast<off_t>(liDistanceToMove), origin) < 0) {
|
||||
if (position < 0) {
|
||||
if (errno == EINVAL) {
|
||||
wibo::lastError = ERROR_NEGATIVE_SEEK;
|
||||
} else {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
return FALSE;
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
off_t position = ftello(fp);
|
||||
file->filePos = position;
|
||||
if (position < 0) {
|
||||
setLastErrorFromErrno();
|
||||
if (errno == EINVAL) {
|
||||
wibo::lastError = ERROR_NEGATIVE_SEEK;
|
||||
} else {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
if (lpNewFilePointer) {
|
||||
@ -807,12 +953,19 @@ BOOL WIN_FUNC SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLA
|
||||
|
||||
BOOL WIN_FUNC SetEndOfFile(HANDLE hFile) {
|
||||
DEBUG_LOG("SetEndOfFile(%p)\n", hFile);
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if (fflush(fp) != 0 || ftruncate(fileno(fp), ftell(fp)) != 0) {
|
||||
// TODO access check
|
||||
std::lock_guard lk(file->m);
|
||||
if (file->filePos < 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
if (ftruncate64(file->fd, file->filePos) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -857,20 +1010,29 @@ BOOL WIN_FUNC SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
DEBUG_LOG("SetFileAttributesA(%s, %u)\n", lpFileName, dwFileAttributes);
|
||||
DEBUG_LOG("STUB: SetFileAttributesA(%s, %u)\n", lpFileName, dwFileAttributes);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
|
||||
DEBUG_LOG("GetFileSize(%p, %p) ", hFile, lpFileSizeHigh);
|
||||
int64_t size = getFileSizeFromHandle(hFile);
|
||||
// TODO access check
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
DEBUG_LOG("-> INVALID_FILE_SIZE (ERROR_INVALID_HANDLE)\n");
|
||||
return INVALID_FILE_SIZE;
|
||||
}
|
||||
const auto size = lseek64(file->fd, 0, SEEK_END);
|
||||
if (size < 0) {
|
||||
if (lpFileSizeHigh) {
|
||||
*lpFileSizeHigh = 0;
|
||||
}
|
||||
DEBUG_LOG("-> INVALID_FILE_SIZE\n");
|
||||
return INVALID_FILE_SIZE;
|
||||
}
|
||||
DEBUG_LOG("-> %lld\n", size);
|
||||
uint64_t uSize = static_cast<uint64_t>(size);
|
||||
if (lpFileSizeHigh) {
|
||||
*lpFileSizeHigh = static_cast<DWORD>(uSize >> 32);
|
||||
@ -883,18 +1045,20 @@ BOOL WIN_FUNC GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lp
|
||||
LPFILETIME lpLastWriteTime) {
|
||||
DEBUG_LOG("GetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
#ifdef CHECK_ACCESS
|
||||
if ((meta.grantedAccess & FILE_READ_ATTRIBUTES) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct stat st{};
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
if (fstat(file->fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -923,15 +1087,17 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
const FILETIME *lpLastWriteTime) {
|
||||
DEBUG_LOG("SetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile, &meta);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
#ifdef CHECK_ACCESS
|
||||
if ((meta.grantedAccess & FILE_WRITE_ATTRIBUTES) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool changeAccess = !shouldIgnoreFileTimeParam(lpLastAccessTime);
|
||||
bool changeWrite = !shouldIgnoreFileTimeParam(lpLastWriteTime);
|
||||
@ -940,7 +1106,7 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
return TRUE;
|
||||
}
|
||||
struct stat st{};
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
if (fstat(file->fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -970,13 +1136,13 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
tv[0].tv_usec = accessSpec.tv_nsec / 1000L;
|
||||
tv[1].tv_sec = writeSpec.tv_sec;
|
||||
tv[1].tv_usec = writeSpec.tv_nsec / 1000L;
|
||||
if (futimes(fd, tv) != 0) {
|
||||
if (futimes(file->fd, tv) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
#else
|
||||
struct timespec times[2] = {accessSpec, writeSpec};
|
||||
if (futimens(file->host_fd, times) != 0) {
|
||||
if (futimens(file->fd, times) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
@ -990,48 +1156,52 @@ BOOL WIN_FUNC SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FI
|
||||
|
||||
BOOL WIN_FUNC GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation) {
|
||||
DEBUG_LOG("GetFileInformationByHandle(%p, %p)\n", hFile, lpFileInformation);
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (fp == nullptr) {
|
||||
if (!lpFileInformation) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
HandleMeta meta{};
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile, &meta);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
// TODO access check
|
||||
struct stat64 st{};
|
||||
if (fstat64(fileno(fp), &st) != 0) {
|
||||
if (fstat64(file->fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
if (lpFileInformation) {
|
||||
lpFileInformation->dwFileAttributes = 0;
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
lpFileInformation->ftCreationTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->ftLastAccessTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->ftLastWriteTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->dwVolumeSerialNumber = 0;
|
||||
lpFileInformation->nFileSizeHigh = static_cast<DWORD>(static_cast<uint64_t>(st.st_size) >> 32);
|
||||
lpFileInformation->nFileSizeLow = static_cast<DWORD>(st.st_size & 0xFFFFFFFFULL);
|
||||
lpFileInformation->nNumberOfLinks = 0;
|
||||
lpFileInformation->nFileIndexHigh = 0;
|
||||
lpFileInformation->nFileIndexLow = 0;
|
||||
lpFileInformation->dwFileAttributes = 0;
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
lpFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
lpFileInformation->ftCreationTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->ftLastAccessTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->ftLastWriteTime = kDefaultFileInformationTime;
|
||||
lpFileInformation->dwVolumeSerialNumber = 0;
|
||||
lpFileInformation->nFileSizeHigh = static_cast<DWORD>(static_cast<uint64_t>(st.st_size) >> 32);
|
||||
lpFileInformation->nFileSizeLow = static_cast<DWORD>(st.st_size & 0xFFFFFFFFULL);
|
||||
lpFileInformation->nNumberOfLinks = 0;
|
||||
lpFileInformation->nFileIndexHigh = 0;
|
||||
lpFileInformation->nFileIndexLow = 0;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC GetFileType(HANDLE hFile) {
|
||||
DEBUG_LOG("GetFileType(%p) ", hFile);
|
||||
auto file = wibo::handles().getAs<files::FileObject>(hFile);
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
DEBUG_LOG("-> ERROR_INVALID_HANDLE\n");
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
}
|
||||
struct stat st{};
|
||||
if (fstat(file->host_fd, &st) != 0) {
|
||||
if (fstat(file->fd, &st) != 0) {
|
||||
setLastErrorFromErrno();
|
||||
DEBUG_LOG("-> fstat error\n");
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
|
@ -19,10 +19,6 @@ struct BY_HANDLE_FILE_INFORMATION {
|
||||
using PBY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION *;
|
||||
using LPBY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION *;
|
||||
|
||||
constexpr DWORD FILE_SHARE_READ = 0x00000001;
|
||||
constexpr DWORD FILE_SHARE_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_SHARE_DELETE = 0x00000004;
|
||||
|
||||
constexpr DWORD CREATE_NEW = 1;
|
||||
constexpr DWORD CREATE_ALWAYS = 2;
|
||||
constexpr DWORD OPEN_EXISTING = 3;
|
||||
|
@ -1,11 +1,8 @@
|
||||
#include "handleapi.h"
|
||||
|
||||
#include "dll/advapi32/internal.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "processes.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
@ -17,7 +14,6 @@ BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
|
||||
DEBUG_LOG("DuplicateHandle(%p, %p, %p, %p, %x, %d, %x)\n", hSourceProcessHandle, hSourceHandle,
|
||||
hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
|
||||
(void)dwDesiredAccess;
|
||||
(void)bInheritHandle;
|
||||
(void)dwOptions;
|
||||
if (!lpTargetHandle) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
@ -25,15 +21,10 @@ BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
|
||||
}
|
||||
|
||||
auto validateProcessHandle = [&](HANDLE handle) -> bool {
|
||||
uintptr_t raw = reinterpret_cast<uintptr_t>(handle);
|
||||
if (raw == static_cast<uintptr_t>(-1)) {
|
||||
if (reinterpret_cast<uintptr_t>(handle) == kPseudoCurrentProcessHandleValue) {
|
||||
return true;
|
||||
}
|
||||
auto data = handles::dataFromHandle(handle, false);
|
||||
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto *proc = reinterpret_cast<processes::Process *>(data.ptr);
|
||||
auto proc = wibo::handles().getAs<ProcessObject>(handle);
|
||||
return proc && proc->pid == getpid();
|
||||
};
|
||||
|
||||
@ -44,125 +35,35 @@ BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto file = files::fileHandleFromHandle(hSourceHandle);
|
||||
if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
|
||||
HANDLE handle = files::duplicateFileHandle(file, false);
|
||||
DEBUG_LOG("DuplicateHandle: duplicated std handle -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uintptr_t sourceHandleRaw = reinterpret_cast<uintptr_t>(hSourceHandle);
|
||||
if (sourceHandleRaw == static_cast<uintptr_t>(-1)) {
|
||||
HANDLE handle = processes::allocProcessHandle(getpid());
|
||||
processes::Process *proc = processes::processFromHandle(handle, false);
|
||||
if (proc) {
|
||||
proc->exitCode = STILL_ACTIVE;
|
||||
proc->forcedExitCode = STILL_ACTIVE;
|
||||
proc->terminationRequested = false;
|
||||
}
|
||||
if (sourceHandleRaw == kPseudoCurrentProcessHandleValue) {
|
||||
auto po = make_pin<ProcessObject>(getpid(), -1);
|
||||
auto handle = wibo::handles().alloc(std::move(po), 0, 0);
|
||||
DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
if (sourceHandleRaw == kPseudoCurrentThreadHandleValue) {
|
||||
ThreadObject *obj = ensureCurrentThreadObject();
|
||||
if (obj) {
|
||||
retainThreadObject(obj);
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, obj, 0});
|
||||
DEBUG_LOG("DuplicateHandle: duplicated pseudo current thread -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
ThreadObject *syntheticObj = new ThreadObject();
|
||||
syntheticObj->thread = pthread_self();
|
||||
syntheticObj->finished = false;
|
||||
syntheticObj->joined = false;
|
||||
syntheticObj->detached = true;
|
||||
syntheticObj->synthetic = true;
|
||||
syntheticObj->exitCode = 0;
|
||||
syntheticObj->refCount = 1;
|
||||
syntheticObj->suspendCount = 0;
|
||||
pthread_mutex_init(&syntheticObj->mutex, nullptr);
|
||||
pthread_cond_init(&syntheticObj->cond, nullptr);
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, syntheticObj, 0});
|
||||
DEBUG_LOG("DuplicateHandle: created synthetic thread handle -> %p\n", handle);
|
||||
} else if (sourceHandleRaw == kPseudoCurrentThreadHandleValue) {
|
||||
auto th = make_pin<ThreadObject>(pthread_self());
|
||||
auto handle = wibo::handles().alloc(std::move(th), 0, 0);
|
||||
DEBUG_LOG("DuplicateHandle: created thread handle for current thread -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
handles::Data data = handles::dataFromHandle(hSourceHandle, false);
|
||||
if (data.type == handles::TYPE_PROCESS && data.ptr) {
|
||||
auto *original = reinterpret_cast<processes::Process *>(data.ptr);
|
||||
HANDLE handle = processes::allocProcessHandle(original->pid);
|
||||
auto *copy = processes::processFromHandle(handle, false);
|
||||
if (copy) {
|
||||
*copy = *original;
|
||||
}
|
||||
DEBUG_LOG("DuplicateHandle: duplicated process handle -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
if (!wibo::handles().duplicateTo(hSourceHandle, wibo::handles(), *lpTargetHandle, dwDesiredAccess, bInheritHandle,
|
||||
dwOptions)) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if (data.type == handles::TYPE_THREAD && data.ptr) {
|
||||
auto *threadObj = reinterpret_cast<ThreadObject *>(data.ptr);
|
||||
if (!retainThreadObject(threadObj)) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, threadObj, 0});
|
||||
DEBUG_LOG("DuplicateHandle: duplicated thread handle -> %p\n", handle);
|
||||
*lpTargetHandle = handle;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
DEBUG_LOG("DuplicateHandle: unsupported handle type for %p\n", hSourceHandle);
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC CloseHandle(HANDLE hObject) {
|
||||
DEBUG_LOG("CloseHandle(%p)\n", hObject);
|
||||
auto data = handles::dataFromHandle(hObject, true);
|
||||
if (data.type == handles::TYPE_UNUSED || data.ptr == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
bool success = true;
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
auto file = reinterpret_cast<files::FileHandle *>(data.ptr);
|
||||
if (file) {
|
||||
if (file->closeOnDestroy && file->fp && !(file->fp == stdin || file->fp == stdout || file->fp == stderr)) {
|
||||
fclose(file->fp);
|
||||
}
|
||||
delete file;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
} else if (data.type == handles::TYPE_MAPPED) {
|
||||
if (!closeFileMappingHandle(data.ptr)) {
|
||||
success = false;
|
||||
}
|
||||
} else if (data.type == handles::TYPE_PROCESS) {
|
||||
delete reinterpret_cast<processes::Process *>(data.ptr);
|
||||
} else if (data.type == handles::TYPE_TOKEN) {
|
||||
delete reinterpret_cast<TokenObject *>(data.ptr);
|
||||
} else if (data.type == handles::TYPE_MUTEX) {
|
||||
releaseMutexObject(reinterpret_cast<MutexObject *>(data.ptr));
|
||||
} else if (data.type == handles::TYPE_EVENT) {
|
||||
releaseEventObject(reinterpret_cast<EventObject *>(data.ptr));
|
||||
} else if (data.type == handles::TYPE_THREAD) {
|
||||
releaseThreadObject(reinterpret_cast<ThreadObject *>(data.ptr));
|
||||
} else if (data.type == handles::TYPE_SEMAPHORE) {
|
||||
releaseSemaphoreObject(reinterpret_cast<SemaphoreObject *>(data.ptr));
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
if (!success) {
|
||||
if (!wibo::handles().release(hObject)) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -7,71 +7,36 @@
|
||||
#include <algorithm>
|
||||
#include <mimalloc.h>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <sys/mman.h>
|
||||
|
||||
using kernel32::HeapObject;
|
||||
|
||||
namespace {
|
||||
|
||||
struct HeapRecord {
|
||||
mi_heap_t *heap = nullptr;
|
||||
DWORD createFlags = 0;
|
||||
SIZE_T initialSize = 0;
|
||||
SIZE_T maximumSize = 0;
|
||||
DWORD compatibility = 0;
|
||||
bool isProcessHeap = false;
|
||||
};
|
||||
|
||||
std::once_flag g_processHeapInitFlag;
|
||||
HANDLE g_processHeapHandle = nullptr;
|
||||
HeapRecord *g_processHeapRecord = nullptr;
|
||||
HeapObject *g_processHeapRecord = nullptr;
|
||||
|
||||
void ensureProcessHeapInitialized() {
|
||||
std::call_once(g_processHeapInitFlag, []() {
|
||||
mi_heap_t *heap = mi_heap_get_default();
|
||||
auto *record = new (std::nothrow) HeapRecord{};
|
||||
auto record = make_pin<HeapObject>(heap);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
record->heap = heap;
|
||||
record->isProcessHeap = true;
|
||||
g_processHeapRecord = record;
|
||||
g_processHeapHandle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0});
|
||||
g_processHeapRecord = record.get();
|
||||
g_processHeapHandle = wibo::handles().alloc(std::move(record), 0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
HeapRecord *activeHeapRecord(HANDLE hHeap) {
|
||||
if (!hHeap) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
ensureProcessHeapInitialized();
|
||||
auto data = handles::dataFromHandle(hHeap, false);
|
||||
if (data.type != handles::TYPE_HEAP || data.ptr == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return static_cast<HeapRecord *>(data.ptr);
|
||||
}
|
||||
|
||||
HeapRecord *popHeapRecord(HANDLE hHeap) {
|
||||
ensureProcessHeapInitialized();
|
||||
auto preview = handles::dataFromHandle(hHeap, false);
|
||||
if (preview.type != handles::TYPE_HEAP || preview.ptr == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hHeap, true);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return static_cast<HeapRecord *>(data.ptr);
|
||||
}
|
||||
|
||||
bool isExecutableHeap(const HeapRecord *record) {
|
||||
bool isExecutableHeap(const HeapObject *record) {
|
||||
return record && ((record->createFlags & HEAP_CREATE_ENABLE_EXECUTE) != 0);
|
||||
}
|
||||
|
||||
LPVOID heapAllocFromRecord(HeapRecord *record, DWORD dwFlags, SIZE_T dwBytes) {
|
||||
if (!record) {
|
||||
LPVOID heapAllocFromRecord(HeapObject *record, DWORD dwFlags, SIZE_T dwBytes) {
|
||||
if (!record || !record->heap) {
|
||||
return nullptr;
|
||||
}
|
||||
if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) {
|
||||
@ -95,6 +60,19 @@ LPVOID heapAllocFromRecord(HeapRecord *record, DWORD dwFlags, SIZE_T dwBytes) {
|
||||
|
||||
} // namespace
|
||||
|
||||
HeapObject::~HeapObject() {
|
||||
if (heap) {
|
||||
if (!isProcessHeap) {
|
||||
mi_heap_destroy(heap);
|
||||
}
|
||||
heap = nullptr;
|
||||
}
|
||||
if (isProcessHeap) {
|
||||
g_processHeapHandle = nullptr;
|
||||
g_processHeapRecord = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace kernel32 {
|
||||
|
||||
HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) {
|
||||
@ -110,40 +88,32 @@ HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaxim
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *record = new (std::nothrow) HeapRecord{};
|
||||
if (!record) {
|
||||
mi_heap_delete(heap);
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto record = make_pin<HeapObject>(heap);
|
||||
record->heap = heap;
|
||||
record->createFlags = flOptions;
|
||||
record->initialSize = dwInitialSize;
|
||||
record->maximumSize = dwMaximumSize;
|
||||
record->isProcessHeap = false;
|
||||
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0});
|
||||
HANDLE handle = wibo::handles().alloc(std::move(record), 0, 0);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return handle;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC HeapDestroy(HANDLE hHeap) {
|
||||
DEBUG_LOG("HeapDestroy(%p)\n", hHeap);
|
||||
HeapRecord *record = activeHeapRecord(hHeap);
|
||||
auto record = wibo::handles().getAs<HeapObject>(hHeap);
|
||||
if (!record) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if (record->isProcessHeap) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
record = popHeapRecord(hHeap);
|
||||
if (!record) {
|
||||
std::lock_guard lk(record->m);
|
||||
if (record->isProcessHeap || record->heap == nullptr) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
mi_heap_destroy(record->heap);
|
||||
delete record;
|
||||
record->heap = nullptr;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -159,18 +129,23 @@ BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapI
|
||||
SIZE_T HeapInformationLength) {
|
||||
DEBUG_LOG("HeapSetInformation(%p, %d, %p, %zu)\n", HeapHandle, static_cast<int>(HeapInformationClass),
|
||||
HeapInformation, HeapInformationLength);
|
||||
ensureProcessHeapInitialized();
|
||||
auto record = wibo::handles().getAs<HeapObject>(HeapHandle);
|
||||
if (!record) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
std::lock_guard lk(record->m);
|
||||
if (!record->heap) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
switch (HeapInformationClass) {
|
||||
case HeapCompatibilityInformation: {
|
||||
if (!HeapInformation || HeapInformationLength < sizeof(ULONG)) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
HeapRecord *target = HeapHandle ? activeHeapRecord(HeapHandle) : g_processHeapRecord;
|
||||
if (!target) {
|
||||
return FALSE;
|
||||
}
|
||||
target->compatibility = *static_cast<ULONG *>(HeapInformation);
|
||||
record->compatibility = *static_cast<ULONG *>(HeapInformation);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -188,36 +163,51 @@ BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapI
|
||||
|
||||
LPVOID WIN_FUNC HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) {
|
||||
DEBUG_LOG("HeapAlloc(%p, 0x%x, %zu) ", hHeap, dwFlags, dwBytes);
|
||||
HeapRecord *record = activeHeapRecord(hHeap);
|
||||
auto record = wibo::handles().getAs<HeapObject>(hHeap);
|
||||
if (!record) {
|
||||
DEBUG_LOG("-> NULL\n");
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
void *mem = heapAllocFromRecord(record, dwFlags, dwBytes);
|
||||
std::lock_guard lk(record->m);
|
||||
if (!record->heap) {
|
||||
DEBUG_LOG("-> NULL\n");
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
void *mem = heapAllocFromRecord(record.get(), dwFlags, dwBytes);
|
||||
DEBUG_LOG("-> %p\n", mem);
|
||||
return mem;
|
||||
}
|
||||
|
||||
LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) {
|
||||
DEBUG_LOG("HeapReAlloc(%p, 0x%x, %p, %zu) ", hHeap, dwFlags, lpMem, dwBytes);
|
||||
HeapRecord *record = activeHeapRecord(hHeap);
|
||||
auto record = wibo::handles().getAs<HeapObject>(hHeap);
|
||||
if (!record) {
|
||||
DEBUG_LOG("-> NULL\n");
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lk(record->m);
|
||||
if (!record->heap) {
|
||||
DEBUG_LOG("-> NULL\n");
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
if (lpMem == nullptr) {
|
||||
void *alloc = heapAllocFromRecord(record, dwFlags, dwBytes);
|
||||
void *alloc = heapAllocFromRecord(record.get(), dwFlags, dwBytes);
|
||||
DEBUG_LOG("-> %p (alloc)\n", alloc);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return alloc;
|
||||
}
|
||||
if (!mi_heap_check_owned(record->heap, lpMem)) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
DEBUG_LOG("-> NULL (not owned)\n");
|
||||
return nullptr;
|
||||
}
|
||||
// if (!mi_heap_check_owned(record->heap, lpMem)) {
|
||||
// DEBUG_LOG("-> NULL (not owned)\n");
|
||||
// wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
// return nullptr;
|
||||
// }
|
||||
if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) {
|
||||
DEBUG_LOG("-> NULL (exceptions unsupported)\n");
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return nullptr;
|
||||
}
|
||||
const bool inplaceOnly = (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) != 0;
|
||||
@ -225,12 +215,12 @@ LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dw
|
||||
if (dwBytes == 0) {
|
||||
if (!inplaceOnly) {
|
||||
mi_free(lpMem);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
DEBUG_LOG("-> NULL (freed)\n");
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return nullptr;
|
||||
}
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
DEBUG_LOG("-> NULL (zero size with in-place flag)\n");
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -238,12 +228,12 @@ LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dw
|
||||
const SIZE_T oldSize = mi_usable_size(lpMem);
|
||||
if (inplaceOnly || requestSize <= oldSize) {
|
||||
if (requestSize > oldSize) {
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
DEBUG_LOG("-> NULL (cannot grow in place)\n");
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
DEBUG_LOG("-> %p (in-place)\n", lpMem);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return lpMem;
|
||||
}
|
||||
|
||||
@ -259,7 +249,7 @@ LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dw
|
||||
memset(static_cast<char *>(ret) + oldSize, 0, zeroLen);
|
||||
}
|
||||
}
|
||||
if (isExecutableHeap(record)) {
|
||||
if (isExecutableHeap(record.get())) {
|
||||
tryMarkExecutable(ret);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
@ -270,18 +260,24 @@ LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dw
|
||||
SIZE_T WIN_FUNC HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) {
|
||||
DEBUG_LOG("HeapSize(%p, 0x%x, %p)\n", hHeap, dwFlags, lpMem);
|
||||
(void)dwFlags;
|
||||
HeapRecord *record = activeHeapRecord(hHeap);
|
||||
auto record = wibo::handles().getAs<HeapObject>(hHeap);
|
||||
if (!record) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return static_cast<SIZE_T>(-1);
|
||||
}
|
||||
std::lock_guard lk(record->m);
|
||||
if (!record->heap) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return static_cast<SIZE_T>(-1);
|
||||
}
|
||||
if (!lpMem) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return static_cast<SIZE_T>(-1);
|
||||
}
|
||||
if (!mi_heap_check_owned(record->heap, const_cast<LPVOID>(lpMem))) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return static_cast<SIZE_T>(-1);
|
||||
}
|
||||
// if (!mi_heap_check_owned(record->heap, const_cast<LPVOID>(lpMem))) {
|
||||
// wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
// return static_cast<SIZE_T>(-1);
|
||||
// }
|
||||
size_t size = mi_usable_size(lpMem);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return static_cast<SIZE_T>(size);
|
||||
@ -294,15 +290,24 @@ BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
HeapRecord *record = activeHeapRecord(hHeap);
|
||||
auto record = wibo::handles().getAs<HeapObject>(hHeap);
|
||||
if (!record) {
|
||||
DEBUG_LOG("-> INVALID_HANDLE\n");
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
if (!mi_heap_check_owned(record->heap, lpMem)) {
|
||||
std::lock_guard lk(record->m);
|
||||
if (!record->heap) {
|
||||
DEBUG_LOG("-> INVALID_PARAMETER\n");
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
// if (!mi_heap_check_owned(record->heap, lpMem)) {
|
||||
// wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
// return FALSE;
|
||||
// }
|
||||
mi_free(lpMem);
|
||||
DEBUG_LOG("-> SUCCESS\n");
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -1,68 +1,172 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "minwinbase.h"
|
||||
#include "handles.h"
|
||||
#include "mimalloc.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
|
||||
namespace kernel32 {
|
||||
|
||||
struct ThreadObject {
|
||||
pthread_t thread{};
|
||||
bool finished = false;
|
||||
bool joined = false;
|
||||
bool detached = false;
|
||||
bool synthetic = false;
|
||||
DWORD exitCode = 0;
|
||||
int refCount = 1;
|
||||
pthread_mutex_t mutex{};
|
||||
pthread_cond_t cond{};
|
||||
struct FsObject : ObjectBase {
|
||||
std::mutex m;
|
||||
int fd = -1;
|
||||
std::filesystem::path canonicalPath;
|
||||
uint32_t shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
bool deletePending = false;
|
||||
bool closeOnDestroy = true;
|
||||
|
||||
~FsObject() override;
|
||||
[[nodiscard]] bool valid() const { return fd >= 0; }
|
||||
|
||||
protected:
|
||||
explicit FsObject(ObjectType type, int fd) : ObjectBase(type), fd(fd) {}
|
||||
};
|
||||
|
||||
struct FileObject final : FsObject {
|
||||
static constexpr ObjectType kType = ObjectType::File;
|
||||
|
||||
off64_t filePos = 0;
|
||||
bool overlapped = false;
|
||||
bool appendOnly = false;
|
||||
bool seekable = true;
|
||||
|
||||
explicit FileObject(int fd) : FsObject(kType, fd) {
|
||||
if (fd >= 0) {
|
||||
off64_t pos = lseek64(fd, 0, SEEK_CUR);
|
||||
if (pos == -1 && errno == ESPIPE) {
|
||||
seekable = false;
|
||||
} else if (pos >= 0) {
|
||||
filePos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DirectoryObject final : FsObject {
|
||||
static constexpr ObjectType kType = ObjectType::Directory;
|
||||
|
||||
uint64_t enumCookie = 0;
|
||||
|
||||
explicit DirectoryObject(int dirfd) : FsObject(kType, dirfd) {}
|
||||
};
|
||||
|
||||
struct ProcessObject final : WaitableObject {
|
||||
static constexpr ObjectType kType = ObjectType::Process;
|
||||
|
||||
pid_t pid;
|
||||
int pidfd;
|
||||
DWORD exitCode = STILL_ACTIVE;
|
||||
bool forcedExitCode = false;
|
||||
|
||||
explicit ProcessObject(pid_t pid, int pidfd) : WaitableObject(kType), pid(pid), pidfd(pidfd) {}
|
||||
|
||||
~ProcessObject() override {
|
||||
std::lock_guard lk(m);
|
||||
if (pidfd != -1) {
|
||||
close(pidfd);
|
||||
pidfd = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreadObject final : WaitableObject {
|
||||
static constexpr ObjectType kType = ObjectType::Thread;
|
||||
|
||||
pthread_t thread;
|
||||
DWORD exitCode = STILL_ACTIVE;
|
||||
unsigned int suspendCount = 0;
|
||||
TIB *tib = nullptr;
|
||||
|
||||
explicit ThreadObject(pthread_t thread) : WaitableObject(kType), thread(thread) {}
|
||||
|
||||
~ThreadObject() override {
|
||||
std::lock_guard lk(m);
|
||||
// Threads are detached at creation; we can safely drop
|
||||
if (tib) {
|
||||
wibo::destroyTib(tib);
|
||||
tib = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MutexObject {
|
||||
pthread_mutex_t mutex{};
|
||||
struct MutexObject final : WaitableObject {
|
||||
static constexpr ObjectType kType = ObjectType::Mutex;
|
||||
|
||||
bool ownerValid = false;
|
||||
pthread_t owner = 0;
|
||||
pthread_t owner{};
|
||||
unsigned int recursionCount = 0;
|
||||
std::u16string name;
|
||||
int refCount = 1;
|
||||
bool abandoned = false; // Owner exited without releasing
|
||||
|
||||
MutexObject() : WaitableObject(kType) { signaled.store(true, std::memory_order_relaxed); }
|
||||
|
||||
void noteOwnerExit(/*pthread_t tid or emu tid*/) {
|
||||
std::lock_guard lk(m);
|
||||
if (ownerValid /* && matches tid if you store emu id */) {
|
||||
ownerValid = false;
|
||||
recursionCount = 0;
|
||||
abandoned = true;
|
||||
signaled.store(true, std::memory_order_release);
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct EventObject {
|
||||
pthread_mutex_t mutex{};
|
||||
pthread_cond_t cond{};
|
||||
struct EventObject final : WaitableObject {
|
||||
static constexpr ObjectType kType = ObjectType::Event;
|
||||
|
||||
bool manualReset = false;
|
||||
bool signaled = false;
|
||||
std::u16string name;
|
||||
int refCount = 1;
|
||||
|
||||
explicit EventObject(bool manual) : WaitableObject(kType), manualReset(manual) {}
|
||||
|
||||
void set() {
|
||||
bool resetAll = false;
|
||||
{
|
||||
std::lock_guard lk(m);
|
||||
signaled.store(true, std::memory_order_release);
|
||||
resetAll = manualReset;
|
||||
}
|
||||
if (resetAll) {
|
||||
cv.notify_all();
|
||||
} else {
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
std::lock_guard lk(m);
|
||||
signaled.store(false, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
struct SemaphoreObject {
|
||||
pthread_mutex_t mutex{};
|
||||
pthread_cond_t cond{};
|
||||
struct SemaphoreObject final : WaitableObject {
|
||||
static constexpr ObjectType kType = ObjectType::Semaphore;
|
||||
|
||||
LONG count = 0;
|
||||
LONG maxCount = 0;
|
||||
std::u16string name;
|
||||
int refCount = 1;
|
||||
|
||||
SemaphoreObject(LONG initial, LONG maximum) : WaitableObject(kType), count(initial), maxCount(maximum) {}
|
||||
};
|
||||
|
||||
struct HeapObject : public ObjectBase {
|
||||
static constexpr ObjectType kType = ObjectType::Heap;
|
||||
|
||||
std::mutex m;
|
||||
mi_heap_t *heap;
|
||||
DWORD createFlags = 0;
|
||||
SIZE_T initialSize = 0;
|
||||
SIZE_T maximumSize = 0;
|
||||
DWORD compatibility = 0;
|
||||
bool isProcessHeap = false;
|
||||
|
||||
explicit HeapObject(mi_heap_t *heap) : ObjectBase(kType), heap(heap) {}
|
||||
~HeapObject() override;
|
||||
};
|
||||
|
||||
inline constexpr uintptr_t kPseudoCurrentProcessHandleValue = static_cast<uintptr_t>(-1);
|
||||
inline constexpr uintptr_t kPseudoCurrentThreadHandleValue = static_cast<uintptr_t>(-2);
|
||||
|
||||
void releaseMutexObject(MutexObject *obj);
|
||||
void releaseEventObject(EventObject *obj);
|
||||
void releaseSemaphoreObject(SemaphoreObject *obj);
|
||||
void resetOverlappedEvent(OVERLAPPED *ov);
|
||||
void signalOverlappedEvent(OVERLAPPED *ov);
|
||||
void tryMarkExecutable(void *mem);
|
||||
void setLastErrorFromErrno();
|
||||
bool closeFileMappingHandle(void *mappingPtr);
|
||||
int64_t getFileSizeFromHandle(HANDLE hFile);
|
||||
ThreadObject *ensureCurrentThreadObject();
|
||||
ThreadObject *threadObjectFromHandle(HANDLE hThread);
|
||||
ThreadObject *retainThreadObject(ThreadObject *obj);
|
||||
void releaseThreadObject(ThreadObject *obj);
|
||||
|
||||
} // namespace kernel32
|
||||
|
@ -13,18 +13,13 @@ BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWO
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
if (bWait && lpOverlapped->Internal == STATUS_PENDING) {
|
||||
if (lpOverlapped->hEvent) {
|
||||
WaitForSingleObject(lpOverlapped->hEvent, 0xFFFFFFFF);
|
||||
}
|
||||
if (bWait && lpOverlapped->Internal == STATUS_PENDING && lpOverlapped->hEvent) {
|
||||
WaitForSingleObject(lpOverlapped->hEvent, INFINITE);
|
||||
}
|
||||
|
||||
const auto status = static_cast<NTSTATUS>(lpOverlapped->Internal);
|
||||
if (status == STATUS_PENDING) {
|
||||
wibo::lastError = ERROR_IO_INCOMPLETE;
|
||||
if (lpNumberOfBytesTransferred) {
|
||||
*lpNumberOfBytesTransferred = static_cast<DWORD>(lpOverlapped->InternalHigh);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -36,7 +31,7 @@ BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWO
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
if (status == STATUS_END_OF_FILE || status == ERROR_HANDLE_EOF) {
|
||||
if (status == STATUS_END_OF_FILE) {
|
||||
wibo::lastError = ERROR_HANDLE_EOF;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
#include "memoryapi.h"
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "strutil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <iterator>
|
||||
@ -23,21 +21,34 @@ namespace {
|
||||
constexpr size_t kVirtualAllocationGranularity = 64 * 1024;
|
||||
constexpr uintptr_t kProcessAddressLimit = 0x80000000;
|
||||
|
||||
struct MappingObject {
|
||||
struct MappingObject : ObjectBase {
|
||||
static constexpr ObjectType kType = ObjectType::Mapping;
|
||||
|
||||
std::mutex m;
|
||||
int fd = -1;
|
||||
size_t maxSize = 0;
|
||||
DWORD protect = 0;
|
||||
bool anonymous = false;
|
||||
bool closed = false;
|
||||
size_t refCount = 0;
|
||||
|
||||
explicit MappingObject() : ObjectBase(kType) {}
|
||||
~MappingObject() override;
|
||||
};
|
||||
|
||||
MappingObject::~MappingObject() {
|
||||
std::lock_guard lk(m);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewInfo {
|
||||
uintptr_t viewBase = 0;
|
||||
size_t viewLength = 0;
|
||||
uintptr_t allocationBase = 0;
|
||||
size_t allocationLength = 0;
|
||||
MappingObject *owner = nullptr;
|
||||
Pin<MappingObject> owner;
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
DWORD allocationProtect = PAGE_NOACCESS;
|
||||
DWORD type = MEM_PRIVATE;
|
||||
@ -46,26 +57,6 @@ struct ViewInfo {
|
||||
std::map<uintptr_t, ViewInfo> g_viewInfo;
|
||||
std::mutex g_viewInfoMutex;
|
||||
|
||||
void closeMappingIfPossible(MappingObject *mapping) {
|
||||
if (!mapping) {
|
||||
return;
|
||||
}
|
||||
if (mapping->fd != -1) {
|
||||
close(mapping->fd);
|
||||
mapping->fd = -1;
|
||||
}
|
||||
delete mapping;
|
||||
}
|
||||
|
||||
void tryReleaseMapping(MappingObject *mapping) {
|
||||
if (!mapping) {
|
||||
return;
|
||||
}
|
||||
if (mapping->closed && mapping->refCount == 0) {
|
||||
closeMappingIfPossible(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
struct VirtualAllocation {
|
||||
uintptr_t base = 0;
|
||||
size_t size = 0;
|
||||
@ -175,9 +166,8 @@ DWORD desiredAccessToProtect(DWORD desiredAccess, DWORD mappingProtect) {
|
||||
if (wantCopy) {
|
||||
wantWrite = true;
|
||||
}
|
||||
const bool supportsWrite =
|
||||
mappingProtect == PAGE_READWRITE || mappingProtect == PAGE_EXECUTE_READWRITE || mappingProtect == PAGE_WRITECOPY ||
|
||||
mappingProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
const bool supportsWrite = mappingProtect == PAGE_READWRITE || mappingProtect == PAGE_EXECUTE_READWRITE ||
|
||||
mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
const bool supportsCopy = mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
|
||||
if (wantCopy && !supportsCopy) {
|
||||
@ -192,7 +182,7 @@ DWORD desiredAccessToProtect(DWORD desiredAccess, DWORD mappingProtect) {
|
||||
}
|
||||
}
|
||||
if (!wantRead && (mappingProtect == PAGE_READONLY || mappingProtect == PAGE_EXECUTE_READ ||
|
||||
mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY)) {
|
||||
mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY)) {
|
||||
wantRead = true;
|
||||
}
|
||||
|
||||
@ -276,7 +266,7 @@ bool moduleRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info)
|
||||
uintptr_t blockStart = sections[matchIndex].base;
|
||||
uintptr_t blockEnd = sections[matchIndex].base + sections[matchIndex].size;
|
||||
DWORD blockProtect = sections[matchIndex].protect;
|
||||
for (size_t prev = matchIndex; prev > 0; ) {
|
||||
for (size_t prev = matchIndex; prev > 0;) {
|
||||
--prev;
|
||||
const auto §ion = sections[prev];
|
||||
if (section.base + section.size != blockStart) {
|
||||
@ -308,7 +298,7 @@ bool moduleRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info)
|
||||
}
|
||||
|
||||
bool mappedViewRegionForAddress(uintptr_t request, uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info) {
|
||||
std::lock_guard<std::mutex> guard(g_viewInfoMutex);
|
||||
std::lock_guard guard(g_viewInfoMutex);
|
||||
if (g_viewInfo.empty()) {
|
||||
return false;
|
||||
}
|
||||
@ -344,7 +334,7 @@ bool mappedViewRegionForAddress(uintptr_t request, uintptr_t pageBase, MEMORY_BA
|
||||
|
||||
bool virtualAllocationRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info) {
|
||||
const size_t pageSize = systemPageSize();
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
std::unique_lock lk(g_virtualAllocMutex);
|
||||
VirtualAllocation *region = lookupRegion(pageBase);
|
||||
if (!region) {
|
||||
uintptr_t regionStart = pageBase;
|
||||
@ -358,7 +348,7 @@ bool virtualAllocationRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMAT
|
||||
if (regionEnd <= regionStart) {
|
||||
regionEnd = regionStart + pageSize;
|
||||
}
|
||||
lock.unlock();
|
||||
lk.unlock();
|
||||
info.BaseAddress = reinterpret_cast<void *>(regionStart);
|
||||
info.AllocationBase = nullptr;
|
||||
info.AllocationProtect = 0;
|
||||
@ -407,7 +397,7 @@ bool virtualAllocationRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMAT
|
||||
uintptr_t allocationBase = region->base;
|
||||
DWORD allocationProtect = region->allocationProtect != 0 ? region->allocationProtect : PAGE_NOACCESS;
|
||||
DWORD finalProtect = committed ? pageProtect : PAGE_NOACCESS;
|
||||
lock.unlock();
|
||||
lk.unlock();
|
||||
info.BaseAddress = reinterpret_cast<void *>(blockStart);
|
||||
info.AllocationBase = reinterpret_cast<void *>(allocationBase);
|
||||
info.AllocationProtect = allocationProtect;
|
||||
@ -477,50 +467,39 @@ HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMap
|
||||
(void)lpFileMappingAttributes;
|
||||
(void)lpName;
|
||||
|
||||
auto *mapping = new MappingObject();
|
||||
mapping->protect = flProtect;
|
||||
|
||||
uint64_t size = (static_cast<uint64_t>(dwMaximumSizeHigh) << 32) | dwMaximumSizeLow;
|
||||
if (flProtect != PAGE_READONLY && flProtect != PAGE_READWRITE && flProtect != PAGE_WRITECOPY) {
|
||||
DEBUG_LOG("CreateFileMappingA: unsupported protection 0x%x\n", flProtect);
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto mapping = make_pin<MappingObject>();
|
||||
mapping->protect = flProtect;
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
mapping->anonymous = true;
|
||||
mapping->fd = -1;
|
||||
if (size == 0) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
mapping->maxSize = size;
|
||||
} else {
|
||||
FILE *fp = files::fpFromHandle(hFile);
|
||||
if (!fp) {
|
||||
auto file = wibo::handles().getAs<FileObject>(hFile);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
int originalFd = fileno(fp);
|
||||
if (originalFd == -1) {
|
||||
setLastErrorFromErrno();
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
int dupFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0);
|
||||
int dupFd = fcntl(file->fd, F_DUPFD_CLOEXEC, 0);
|
||||
if (dupFd == -1) {
|
||||
setLastErrorFromErrno();
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
mapping->fd = dupFd;
|
||||
if (size == 0) {
|
||||
int64_t fileSize = getFileSizeFromHandle(hFile);
|
||||
off64_t fileSize = lseek64(dupFd, 0, SEEK_END);
|
||||
if (fileSize < 0) {
|
||||
closeMappingIfPossible(mapping);
|
||||
return nullptr;
|
||||
}
|
||||
size = static_cast<uint64_t>(fileSize);
|
||||
@ -529,7 +508,7 @@ HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMap
|
||||
}
|
||||
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return handles::allocDataHandle({handles::TYPE_MAPPED, mapping, static_cast<size_t>(mapping->maxSize)});
|
||||
return wibo::handles().alloc(std::move(mapping), 0, 0);
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect,
|
||||
@ -540,7 +519,7 @@ HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMap
|
||||
lpName ? name.c_str() : nullptr);
|
||||
}
|
||||
|
||||
static LPVOID mapViewOfFileInternal(MappingObject *mapping, DWORD dwDesiredAccess, uint64_t offset,
|
||||
static LPVOID mapViewOfFileInternal(Pin<MappingObject> mapping, DWORD dwDesiredAccess, uint64_t offset,
|
||||
SIZE_T dwNumberOfBytesToMap, LPVOID baseAddress) {
|
||||
if (!mapping) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
@ -660,20 +639,20 @@ static LPVOID mapViewOfFileInternal(MappingObject *mapping, DWORD dwDesiredAcces
|
||||
if (alignedViewLength == std::numeric_limits<uintptr_t>::max()) {
|
||||
alignedViewLength = viewLength;
|
||||
}
|
||||
DWORD protect = mapping->protect;
|
||||
ViewInfo view{};
|
||||
view.viewBase = reinterpret_cast<uintptr_t>(viewPtr);
|
||||
view.viewLength = static_cast<size_t>(alignedViewLength);
|
||||
view.allocationBase = reinterpret_cast<uintptr_t>(mapBase);
|
||||
view.allocationLength = mapLength;
|
||||
view.owner = mapping;
|
||||
view.protect = desiredAccessToProtect(dwDesiredAccess, mapping->protect);
|
||||
view.allocationProtect = mapping->protect;
|
||||
view.owner = std::move(mapping);
|
||||
view.protect = desiredAccessToProtect(dwDesiredAccess, protect);
|
||||
view.allocationProtect = protect;
|
||||
view.type = MEM_MAPPED;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(g_viewInfoMutex);
|
||||
g_viewInfo[view.viewBase] = view;
|
||||
std::lock_guard guard(g_viewInfoMutex);
|
||||
g_viewInfo.emplace(view.viewBase, std::move(view));
|
||||
}
|
||||
mapping->refCount++;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return viewPtr;
|
||||
}
|
||||
@ -683,14 +662,13 @@ LPVOID WIN_FUNC MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess,
|
||||
DEBUG_LOG("MapViewOfFile(%p, 0x%x, %u, %u, %zu)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
|
||||
dwFileOffsetLow, dwNumberOfBytesToMap);
|
||||
|
||||
handles::Data data = handles::dataFromHandle(hFileMappingObject, false);
|
||||
if (data.type != handles::TYPE_MAPPED) {
|
||||
auto mapping = wibo::handles().getAs<MappingObject>(hFileMappingObject);
|
||||
if (!mapping) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
|
||||
uint64_t offset = (static_cast<uint64_t>(dwFileOffsetHigh) << 32) | dwFileOffsetLow;
|
||||
return mapViewOfFileInternal(mapping, dwDesiredAccess, offset, dwNumberOfBytesToMap, nullptr);
|
||||
return mapViewOfFileInternal(std::move(mapping), dwDesiredAccess, offset, dwNumberOfBytesToMap, nullptr);
|
||||
}
|
||||
|
||||
LPVOID WIN_FUNC MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh,
|
||||
@ -698,49 +676,34 @@ LPVOID WIN_FUNC MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess
|
||||
DEBUG_LOG("MapViewOfFileEx(%p, 0x%x, %u, %u, %zu, %p)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
|
||||
dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress);
|
||||
|
||||
handles::Data data = handles::dataFromHandle(hFileMappingObject, false);
|
||||
if (data.type != handles::TYPE_MAPPED) {
|
||||
auto mapping = wibo::handles().getAs<MappingObject>(hFileMappingObject);
|
||||
if (!mapping) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
auto *mapping = reinterpret_cast<MappingObject *>(data.ptr);
|
||||
uint64_t offset = (static_cast<uint64_t>(dwFileOffsetHigh) << 32) | dwFileOffsetLow;
|
||||
return mapViewOfFileInternal(mapping, dwDesiredAccess, offset, dwNumberOfBytesToMap, lpBaseAddress);
|
||||
return mapViewOfFileInternal(std::move(mapping), dwDesiredAccess, offset, dwNumberOfBytesToMap, lpBaseAddress);
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress) {
|
||||
DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress);
|
||||
std::unique_lock<std::mutex> lock(g_viewInfoMutex);
|
||||
std::unique_lock lk(g_viewInfoMutex);
|
||||
auto it = g_viewInfo.find(reinterpret_cast<uintptr_t>(lpBaseAddress));
|
||||
if (it == g_viewInfo.end()) {
|
||||
lock.unlock();
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
ViewInfo info = it->second;
|
||||
void *base = reinterpret_cast<void *>(it->second.allocationBase);
|
||||
size_t length = it->second.allocationLength;
|
||||
g_viewInfo.erase(it);
|
||||
lock.unlock();
|
||||
if (info.allocationLength != 0) {
|
||||
munmap(reinterpret_cast<void *>(info.allocationBase), info.allocationLength);
|
||||
}
|
||||
if (info.owner && info.owner->refCount > 0) {
|
||||
info.owner->refCount--;
|
||||
tryReleaseMapping(info.owner);
|
||||
lk.unlock();
|
||||
if (length != 0) {
|
||||
munmap(base, length);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool closeFileMappingHandle(void *mappingPtr) {
|
||||
auto *mapping = reinterpret_cast<MappingObject *>(mappingPtr);
|
||||
if (!mapping) {
|
||||
return false;
|
||||
}
|
||||
mapping->closed = true;
|
||||
tryReleaseMapping(mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
|
||||
DEBUG_LOG("VirtualAlloc(%p, %zu, %u, %u)\n", lpAddress, dwSize, flAllocationType, flProtect);
|
||||
|
||||
@ -782,7 +745,7 @@ LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocation
|
||||
uintptr_t start = alignDown(request, pageSize);
|
||||
uintptr_t end = alignUp(request + static_cast<uintptr_t>(dwSize), pageSize);
|
||||
size_t length = static_cast<size_t>(end - start);
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
std::unique_lock lk(g_virtualAllocMutex);
|
||||
VirtualAllocation *region = lookupRegion(start);
|
||||
if (!region || !rangeWithinRegion(*region, start, length)) {
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
@ -807,7 +770,7 @@ LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocation
|
||||
}
|
||||
|
||||
const size_t pageSize = systemPageSize();
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
std::unique_lock lk(g_virtualAllocMutex);
|
||||
|
||||
if (reserve) {
|
||||
uintptr_t base = 0;
|
||||
@ -950,7 +913,7 @@ BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) {
|
||||
}
|
||||
|
||||
const size_t pageSize = systemPageSize();
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
std::unique_lock lk(g_virtualAllocMutex);
|
||||
|
||||
if (release) {
|
||||
uintptr_t base = reinterpret_cast<uintptr_t>(lpAddress);
|
||||
@ -970,7 +933,7 @@ BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) {
|
||||
}
|
||||
size_t length = exact->second.size;
|
||||
g_virtualAllocations.erase(exact);
|
||||
lock.unlock();
|
||||
lk.unlock();
|
||||
if (munmap(lpAddress, length) != 0) {
|
||||
wibo::lastError = wibo::winErrorFromErrno(errno);
|
||||
return FALSE;
|
||||
@ -1038,7 +1001,7 @@ BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
std::unique_lock lk(g_virtualAllocMutex);
|
||||
VirtualAllocation *region = lookupRegion(start);
|
||||
if (!region || !rangeWithinRegion(*region, start, static_cast<size_t>(end - start))) {
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
@ -1072,7 +1035,7 @@ BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect
|
||||
for (size_t i = 0; i < pageCount; ++i) {
|
||||
region->pageProtect[firstPage + i] = flNewProtect;
|
||||
}
|
||||
lock.unlock();
|
||||
lk.unlock();
|
||||
|
||||
if (lpflOldProtect) {
|
||||
*lpflOldProtect = previousProtect;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "namedpipeapi.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "fileapi.h"
|
||||
#include "files.h"
|
||||
@ -58,39 +59,12 @@ BOOL WIN_FUNC CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRI
|
||||
fcntl(pipeFds[1], F_SETPIPE_SZ, static_cast<int>(nSize));
|
||||
}
|
||||
|
||||
FILE *readStream = fdopen(pipeFds[0], "rb");
|
||||
if (!readStream) {
|
||||
int savedErrno = errno ? errno : EINVAL;
|
||||
close(pipeFds[0]);
|
||||
close(pipeFds[1]);
|
||||
errno = savedErrno;
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
FILE *writeStream = fdopen(pipeFds[1], "wb");
|
||||
if (!writeStream) {
|
||||
int savedErrno = errno ? errno : EINVAL;
|
||||
fclose(readStream);
|
||||
close(pipeFds[1]);
|
||||
errno = savedErrno;
|
||||
setLastErrorFromErrno();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
setvbuf(readStream, nullptr, _IONBF, 0);
|
||||
setvbuf(writeStream, nullptr, _IONBF, 0);
|
||||
|
||||
HANDLE readHandle = files::allocFpHandle(readStream, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, true);
|
||||
HANDLE writeHandle = files::allocFpHandle(writeStream, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, true);
|
||||
if (!readHandle || !writeHandle) {
|
||||
fclose(readStream);
|
||||
fclose(writeStream);
|
||||
wibo::lastError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*hReadPipe = readHandle;
|
||||
*hWritePipe = writeHandle;
|
||||
auto readObj = make_pin<FileObject>(pipeFds[0]);
|
||||
readObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
auto writeObj = make_pin<FileObject>(pipeFds[1]);
|
||||
writeObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
*hReadPipe = wibo::handles().alloc(std::move(readObj), FILE_GENERIC_READ, 0);
|
||||
*hWritePipe = wibo::handles().alloc(std::move(writeObj), FILE_GENERIC_WRITE, 0);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -8,16 +8,20 @@
|
||||
#include "strutil.h"
|
||||
#include "timeutil.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
@ -40,12 +44,6 @@ constexpr DWORD STARTF_USESHOWWINDOW = 0x00000001;
|
||||
constexpr DWORD STARTF_USESTDHANDLES = 0x00000100;
|
||||
constexpr WORD SW_SHOWNORMAL = 1;
|
||||
|
||||
struct ThreadStartData {
|
||||
LPTHREAD_START_ROUTINE startRoutine;
|
||||
void *parameter;
|
||||
ThreadObject *threadObject;
|
||||
};
|
||||
|
||||
FILETIME fileTimeFromTimeval(const struct timeval &value) {
|
||||
uint64_t total = 0;
|
||||
if (value.tv_sec > 0 || value.tv_usec > 0) {
|
||||
@ -62,19 +60,6 @@ FILETIME fileTimeFromTimespec(const struct timespec &value) {
|
||||
return fileTimeFromDuration(total);
|
||||
}
|
||||
|
||||
void destroyThreadObject(ThreadObject *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
if (obj->tib) {
|
||||
wibo::destroyTib(obj->tib);
|
||||
obj->tib = nullptr;
|
||||
}
|
||||
pthread_cond_destroy(&obj->cond);
|
||||
pthread_mutex_destroy(&obj->mutex);
|
||||
delete obj;
|
||||
}
|
||||
|
||||
DWORD_PTR computeSystemAffinityMask() {
|
||||
long reported = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (reported <= 0) {
|
||||
@ -103,6 +88,80 @@ template <typename StartupInfo> void populateStartupInfo(StartupInfo *info) {
|
||||
info->hStdError = files::getStdHandle(STD_ERROR_HANDLE);
|
||||
}
|
||||
|
||||
thread_local ThreadObject *g_currentThreadObject = nullptr;
|
||||
|
||||
struct ThreadStartData {
|
||||
ThreadObject *obj;
|
||||
LPTHREAD_START_ROUTINE entry;
|
||||
void *userData;
|
||||
};
|
||||
|
||||
void threadCleanup(void *param) {
|
||||
ThreadObject *obj = static_cast<ThreadObject *>(param);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lk(obj->m);
|
||||
obj->signaled.store(true, std::memory_order_release);
|
||||
// Exit code set before pthread_exit
|
||||
}
|
||||
g_currentThreadObject = nullptr;
|
||||
wibo::notifyDllThreadDetach();
|
||||
// TODO: mark mutexes owned by this thread as abandoned
|
||||
obj->cv.notify_all();
|
||||
detail::deref(obj);
|
||||
}
|
||||
|
||||
void *threadTrampoline(void *param) {
|
||||
// We ref'd the ThreadObject when constructing ThreadStartData,
|
||||
// so we need to deref it when done. (Either normal exit or via pthread_cleanup)
|
||||
ThreadStartData *dataPtr = static_cast<ThreadStartData *>(param);
|
||||
ThreadStartData data = *dataPtr;
|
||||
delete dataPtr;
|
||||
|
||||
g_currentThreadObject = data.obj;
|
||||
|
||||
// Install TIB
|
||||
TIB *threadTib = nullptr;
|
||||
uint16_t previousSegment = 0;
|
||||
if (wibo::tibSelector) {
|
||||
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
|
||||
threadTib = wibo::allocateTib();
|
||||
if (threadTib) {
|
||||
wibo::initializeTibStackInfo(threadTib);
|
||||
if (wibo::installTibForCurrentThread(threadTib)) {
|
||||
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
|
||||
} else {
|
||||
fprintf(stderr, "!!! Failed to install TIB for new thread\n");
|
||||
wibo::destroyTib(threadTib);
|
||||
threadTib = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until resumed (if suspended at start)
|
||||
{
|
||||
std::unique_lock lk(data.obj->m);
|
||||
data.obj->tib = threadTib;
|
||||
data.obj->cv.wait(lk, [&] { return data.obj->suspendCount == 0; });
|
||||
}
|
||||
|
||||
wibo::notifyDllThreadAttach();
|
||||
DWORD result = data.entry ? data.entry(data.userData) : 0;
|
||||
{
|
||||
std::lock_guard lk(data.obj->m);
|
||||
data.obj->exitCode = result;
|
||||
}
|
||||
threadCleanup(data.obj);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool isPseudoCurrentThreadHandle(HANDLE h) {
|
||||
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(h);
|
||||
return rawHandle == kernel32::kPseudoCurrentThreadHandleValue;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace kernel32 {
|
||||
@ -122,152 +181,6 @@ BOOL WIN_FUNC IsProcessorFeaturePresent(DWORD ProcessorFeature) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
thread_local ThreadObject *g_currentThreadObject = nullptr;
|
||||
|
||||
ThreadObject *ensureCurrentThreadObject() {
|
||||
ThreadObject *obj = g_currentThreadObject;
|
||||
if (obj) {
|
||||
return obj;
|
||||
}
|
||||
obj = new ThreadObject();
|
||||
obj->thread = pthread_self();
|
||||
obj->finished = false;
|
||||
obj->joined = false;
|
||||
obj->detached = true;
|
||||
obj->synthetic = false;
|
||||
obj->exitCode = STILL_ACTIVE;
|
||||
obj->refCount = 0;
|
||||
obj->suspendCount = 0;
|
||||
pthread_mutex_init(&obj->mutex, nullptr);
|
||||
pthread_cond_init(&obj->cond, nullptr);
|
||||
g_currentThreadObject = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
ThreadObject *threadObjectFromHandle(HANDLE hThread) {
|
||||
auto raw = reinterpret_cast<uintptr_t>(hThread);
|
||||
if (raw == kPseudoCurrentThreadHandleValue) {
|
||||
return ensureCurrentThreadObject();
|
||||
}
|
||||
if (raw == static_cast<uintptr_t>(-1) || raw == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hThread, false);
|
||||
if (data.type != handles::TYPE_THREAD || data.ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<ThreadObject *>(data.ptr);
|
||||
}
|
||||
|
||||
ThreadObject *retainThreadObject(ThreadObject *obj) {
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->refCount++;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void releaseThreadObject(ThreadObject *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
pthread_t thread = 0;
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->refCount--;
|
||||
bool shouldDelete = false;
|
||||
bool shouldDetach = false;
|
||||
bool finished = obj->finished;
|
||||
bool joined = obj->joined;
|
||||
bool detached = obj->detached;
|
||||
bool synthetic = obj->synthetic;
|
||||
thread = obj->thread;
|
||||
if (obj->refCount == 0) {
|
||||
if (finished || synthetic) {
|
||||
shouldDelete = true;
|
||||
} else if (!detached) {
|
||||
obj->detached = true;
|
||||
shouldDetach = true;
|
||||
detached = true;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
|
||||
if (shouldDetach && !synthetic) {
|
||||
pthread_detach(thread);
|
||||
}
|
||||
|
||||
if (shouldDelete) {
|
||||
if (!synthetic) {
|
||||
if (!joined && !detached) {
|
||||
pthread_join(thread, nullptr);
|
||||
}
|
||||
}
|
||||
destroyThreadObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void *threadTrampoline(void *param) {
|
||||
ThreadStartData *data = static_cast<ThreadStartData *>(param);
|
||||
ThreadObject *obj = data->threadObject;
|
||||
LPTHREAD_START_ROUTINE startRoutine = data->startRoutine;
|
||||
void *userParam = data->parameter;
|
||||
delete data;
|
||||
|
||||
g_currentThreadObject = obj;
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
while (obj->suspendCount > 0) {
|
||||
pthread_cond_wait(&obj->cond, &obj->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
|
||||
uint16_t previousSegment = 0;
|
||||
bool tibInstalled = false;
|
||||
TIB *threadTib = nullptr;
|
||||
if (wibo::tibSelector) {
|
||||
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
|
||||
threadTib = wibo::allocateTib();
|
||||
if (threadTib) {
|
||||
wibo::initializeTibStackInfo(threadTib);
|
||||
if (wibo::installTibForCurrentThread(threadTib)) {
|
||||
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
|
||||
tibInstalled = true;
|
||||
obj->tib = threadTib;
|
||||
} else {
|
||||
fprintf(stderr, "!!! Failed to install TIB for new thread\n");
|
||||
wibo::destroyTib(threadTib);
|
||||
threadTib = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
wibo::notifyDllThreadAttach();
|
||||
DWORD result = startRoutine ? startRoutine(userParam) : 0;
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->finished = true;
|
||||
obj->exitCode = result;
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
bool shouldDelete = (obj->refCount == 0);
|
||||
bool detached = obj->detached;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::notifyDllThreadDetach();
|
||||
if (tibInstalled) {
|
||||
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
|
||||
}
|
||||
if (threadTib) {
|
||||
obj->tib = nullptr;
|
||||
wibo::destroyTib(threadTib);
|
||||
}
|
||||
g_currentThreadObject = nullptr;
|
||||
|
||||
if (shouldDelete) {
|
||||
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
|
||||
destroyThreadObject(obj);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC GetCurrentProcess() {
|
||||
DEBUG_LOG("GetCurrentProcess() -> %p\n", reinterpret_cast<void *>(static_cast<uintptr_t>(-1)));
|
||||
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
|
||||
@ -287,8 +200,6 @@ DWORD WIN_FUNC GetCurrentThreadId() {
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC GetCurrentThread() {
|
||||
ThreadObject *obj = ensureCurrentThreadObject();
|
||||
(void)obj;
|
||||
HANDLE pseudoHandle = reinterpret_cast<HANDLE>(kPseudoCurrentThreadHandleValue);
|
||||
DEBUG_LOG("GetCurrentThread() -> %p\n", pseudoHandle);
|
||||
return pseudoHandle;
|
||||
@ -303,12 +214,12 @@ BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffini
|
||||
}
|
||||
|
||||
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(hProcess);
|
||||
bool isPseudoHandle = rawHandle == static_cast<uintptr_t>(-1);
|
||||
bool isPseudoHandle = rawHandle == 0 || rawHandle == kPseudoCurrentProcessHandleValue;
|
||||
if (!isPseudoHandle) {
|
||||
auto data = handles::dataFromHandle(hProcess, false);
|
||||
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
|
||||
auto obj = wibo::handles().getAs<ProcessObject>(hProcess);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,12 +247,12 @@ BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinit
|
||||
}
|
||||
|
||||
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(hProcess);
|
||||
bool isPseudoHandle = rawHandle == static_cast<uintptr_t>(-1);
|
||||
bool isPseudoHandle = rawHandle == 0 || rawHandle == kPseudoCurrentProcessHandleValue;
|
||||
if (!isPseudoHandle) {
|
||||
auto data = handles::dataFromHandle(hProcess, false);
|
||||
if (data.type != handles::TYPE_PROCESS) {
|
||||
auto obj = wibo::handles().getAs<ProcessObject>(hProcess);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,12 +275,9 @@ DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffin
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t rawThreadHandle = reinterpret_cast<uintptr_t>(hThread);
|
||||
bool isPseudoHandle = rawThreadHandle == kPseudoCurrentThreadHandleValue || rawThreadHandle == 0 ||
|
||||
rawThreadHandle == static_cast<uintptr_t>(-1);
|
||||
if (!isPseudoHandle) {
|
||||
auto data = handles::dataFromHandle(hThread, false);
|
||||
if (data.type != handles::TYPE_THREAD) {
|
||||
if (!isPseudoCurrentThreadHandle(hThread)) {
|
||||
auto obj = wibo::handles().getAs<ThreadObject>(hThread);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
@ -391,23 +299,27 @@ DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffin
|
||||
|
||||
void WIN_FUNC ExitProcess(UINT uExitCode) {
|
||||
DEBUG_LOG("ExitProcess(%u)\n", uExitCode);
|
||||
std::exit(static_cast<int>(uExitCode));
|
||||
exit(static_cast<int>(uExitCode));
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode) {
|
||||
DEBUG_LOG("TerminateProcess(%p, %u)\n", hProcess, uExitCode);
|
||||
if (hProcess == reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1))) {
|
||||
ExitProcess(uExitCode);
|
||||
exit(static_cast<int>(uExitCode));
|
||||
}
|
||||
auto data = handles::dataFromHandle(hProcess, false);
|
||||
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
|
||||
auto process = wibo::handles().getAs<ProcessObject>(hProcess);
|
||||
if (!process) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
auto *process = reinterpret_cast<processes::Process *>(data.ptr);
|
||||
if (kill(process->pid, SIGKILL) != 0) {
|
||||
if (process->signaled.load(std::memory_order_acquire)) {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
std::lock_guard lk(process->m);
|
||||
if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) {
|
||||
int err = errno;
|
||||
DEBUG_LOG("TerminateProcess: kill(%d) failed: %s\n", process->pid, strerror(err));
|
||||
DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(err));
|
||||
switch (err) {
|
||||
case ESRCH:
|
||||
case EPERM:
|
||||
@ -419,9 +331,8 @@ BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode) {
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
process->forcedExitCode = uExitCode;
|
||||
process->terminationRequested = true;
|
||||
process->exitCode = uExitCode;
|
||||
process->forcedExitCode = true;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -432,12 +343,17 @@ BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
auto *process = processes::processFromHandle(hProcess, false);
|
||||
auto process = wibo::handles().getAs<ProcessObject>(hProcess);
|
||||
if (!process) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
*lpExitCode = process->exitCode;
|
||||
DWORD exitCode = STILL_ACTIVE;
|
||||
if (process->signaled.load(std::memory_order_acquire)) {
|
||||
std::lock_guard lk(process->m);
|
||||
exitCode = process->exitCode;
|
||||
}
|
||||
*lpExitCode = exitCode;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -491,20 +407,27 @@ BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) {
|
||||
|
||||
DWORD WIN_FUNC ResumeThread(HANDLE hThread) {
|
||||
DEBUG_LOG("ResumeThread(%p)\n", hThread);
|
||||
ThreadObject *obj = threadObjectFromHandle(hThread);
|
||||
// TODO: behavior with current thread handle?
|
||||
auto obj = wibo::handles().getAs<ThreadObject>(hThread);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return static_cast<DWORD>(-1);
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
DWORD previous = obj->suspendCount;
|
||||
if (obj->suspendCount > 0) {
|
||||
obj->suspendCount--;
|
||||
if (obj->suspendCount == 0) {
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
DWORD previous = 0;
|
||||
bool notify = false;
|
||||
{
|
||||
std::lock_guard lk(obj->m);
|
||||
previous = obj->suspendCount;
|
||||
if (obj->suspendCount > 0) {
|
||||
obj->suspendCount--;
|
||||
if (obj->suspendCount == 0) {
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
if (notify) {
|
||||
obj->cv.notify_all();
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return previous;
|
||||
}
|
||||
@ -530,48 +453,30 @@ HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dw
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return nullptr;
|
||||
}
|
||||
bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0;
|
||||
|
||||
ThreadObject *obj = new ThreadObject();
|
||||
pthread_mutex_init(&obj->mutex, nullptr);
|
||||
pthread_cond_init(&obj->cond, nullptr);
|
||||
obj->finished = false;
|
||||
obj->joined = false;
|
||||
obj->detached = false;
|
||||
obj->exitCode = 0;
|
||||
obj->refCount = 1;
|
||||
obj->suspendCount = startSuspended ? 1u : 0u;
|
||||
obj->synthetic = false;
|
||||
|
||||
ThreadStartData *startData = new ThreadStartData{lpStartAddress, lpParameter, obj};
|
||||
Pin<ThreadObject> obj = make_pin<ThreadObject>(0); // tid set during pthread_create
|
||||
if ((dwCreationFlags & CREATE_SUSPENDED) != 0) {
|
||||
obj->suspendCount = 1;
|
||||
}
|
||||
detail::ref(obj.get()); // Increment ref for the new thread to adopt
|
||||
ThreadStartData *startData = new ThreadStartData{obj.get(), lpStartAddress, lpParameter};
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_t *attrPtr = nullptr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
if (dwStackSize != 0) {
|
||||
pthread_attr_init(&attr);
|
||||
size_t stackSize = dwStackSize;
|
||||
#ifdef PTHREAD_STACK_MIN
|
||||
if (stackSize < static_cast<size_t>(PTHREAD_STACK_MIN)) {
|
||||
stackSize = PTHREAD_STACK_MIN;
|
||||
}
|
||||
#endif
|
||||
if (pthread_attr_setstacksize(&attr, stackSize) == 0) {
|
||||
attrPtr = &attr;
|
||||
} else {
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
// TODO: should we just ignore this?
|
||||
pthread_attr_setstacksize(&attr, std::max(dwStackSize, static_cast<SIZE_T>(PTHREAD_STACK_MIN)));
|
||||
}
|
||||
|
||||
int rc = pthread_create(&obj->thread, attrPtr, threadTrampoline, startData);
|
||||
if (attrPtr) {
|
||||
pthread_attr_destroy(attrPtr);
|
||||
}
|
||||
int rc = pthread_create(&obj->thread, &attr, threadTrampoline, startData);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (rc != 0) {
|
||||
// Clean up
|
||||
delete startData;
|
||||
destroyThreadObject(obj);
|
||||
errno = rc;
|
||||
setLastErrorFromErrno();
|
||||
return nullptr;
|
||||
detail::deref(obj.get());
|
||||
wibo::lastError = wibo::winErrorFromErrno(rc);
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (lpThreadId) {
|
||||
@ -580,46 +485,19 @@ HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dw
|
||||
}
|
||||
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return handles::allocDataHandle({handles::TYPE_THREAD, obj, 0});
|
||||
return wibo::handles().alloc(std::move(obj), 0 /* TODO */, 0);
|
||||
}
|
||||
|
||||
void WIN_FUNC ExitThread(DWORD dwExitCode) {
|
||||
[[noreturn]] void WIN_FUNC ExitThread(DWORD dwExitCode) {
|
||||
DEBUG_LOG("ExitThread(%u)\n", dwExitCode);
|
||||
ThreadObject *obj = g_currentThreadObject;
|
||||
TIB *threadTib = obj ? obj->tib : nullptr;
|
||||
uint16_t previousSegment = 0;
|
||||
bool tibInstalled = false;
|
||||
if (wibo::tibSelector) {
|
||||
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
|
||||
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
|
||||
tibInstalled = true;
|
||||
}
|
||||
bool shouldDelete = false;
|
||||
bool detached = false;
|
||||
if (obj) {
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->finished = true;
|
||||
{
|
||||
std::lock_guard lk(obj->m);
|
||||
obj->exitCode = dwExitCode;
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
shouldDelete = (obj->refCount == 0);
|
||||
detached = obj->detached;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
}
|
||||
wibo::notifyDllThreadDetach();
|
||||
if (tibInstalled) {
|
||||
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
|
||||
}
|
||||
if (obj && threadTib) {
|
||||
obj->tib = nullptr;
|
||||
wibo::destroyTib(threadTib);
|
||||
}
|
||||
if (obj) {
|
||||
g_currentThreadObject = nullptr;
|
||||
if (shouldDelete) {
|
||||
assert(detached && "ThreadObject must be detached when refCount reaches zero before completion");
|
||||
destroyThreadObject(obj);
|
||||
}
|
||||
}
|
||||
// Can't use pthread_cleanup_push/pop because it can't unwind the Windows stack
|
||||
// So call the cleanup function directly before pthread_exit
|
||||
threadCleanup(obj);
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
@ -629,15 +507,18 @@ BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
ThreadObject *obj = threadObjectFromHandle(hThread);
|
||||
if (isPseudoCurrentThreadHandle(hThread)) {
|
||||
*lpExitCode = STILL_ACTIVE;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
auto obj = wibo::handles().getAs<ThreadObject>(hThread);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
DWORD code = obj->finished ? obj->exitCode : STILL_ACTIVE;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
*lpExitCode = code;
|
||||
std::lock_guard lk(obj->m);
|
||||
*lpExitCode = obj->exitCode;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
@ -673,7 +554,7 @@ BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool isPseudoCurrentThread = reinterpret_cast<uintptr_t>(hThread) == kPseudoCurrentThreadHandleValue ||
|
||||
bool isPseudoCurrentThread = reinterpret_cast<uintptr_t>(hThread) == kernel32::kPseudoCurrentThreadHandleValue ||
|
||||
hThread == nullptr || hThread == reinterpret_cast<HANDLE>(static_cast<uintptr_t>(-1));
|
||||
if (!isPseudoCurrentThread) {
|
||||
DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread);
|
||||
@ -689,7 +570,7 @@ BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME
|
||||
lpExitTime->dwHighDateTime = 0;
|
||||
}
|
||||
|
||||
struct rusage usage;
|
||||
struct rusage usage{};
|
||||
if (getrusage(RUSAGE_THREAD, &usage) == 0) {
|
||||
*lpKernelTime = fileTimeFromTimeval(usage.ru_stime);
|
||||
*lpUserTime = fileTimeFromTimeval(usage.ru_utime);
|
||||
@ -697,7 +578,7 @@ BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct timespec cpuTime;
|
||||
struct timespec cpuTime{};
|
||||
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) {
|
||||
*lpKernelTime = fileTimeFromDuration(0);
|
||||
*lpUserTime = fileTimeFromTimespec(cpuTime);
|
||||
@ -705,7 +586,7 @@ BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
setLastErrorFromErrno();
|
||||
kernel32::setLastErrorFromErrno();
|
||||
*lpKernelTime = fileTimeFromDuration(0);
|
||||
*lpUserTime = fileTimeFromDuration(0);
|
||||
return FALSE;
|
||||
@ -726,7 +607,7 @@ BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSE
|
||||
if (lpApplicationName) {
|
||||
application = lpApplicationName;
|
||||
} else {
|
||||
std::vector<std::string> arguments = processes::splitCommandLine(commandLine.c_str());
|
||||
std::vector<std::string> arguments = wibo::splitCommandLine(commandLine.c_str());
|
||||
if (arguments.empty()) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
return FALSE;
|
||||
@ -734,21 +615,22 @@ BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSE
|
||||
application = arguments.front();
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(application, useSearchPath);
|
||||
auto resolved = wibo::resolveExecutable(application, useSearchPath);
|
||||
if (!resolved) {
|
||||
wibo::lastError = ERROR_FILE_NOT_FOUND;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnWithCommandLine(*resolved, commandLine, &pid);
|
||||
Pin<ProcessObject> obj;
|
||||
int spawnResult = wibo::spawnWithCommandLine(*resolved, commandLine, obj);
|
||||
if (spawnResult != 0) {
|
||||
wibo::lastError = (spawnResult == ENOENT) ? ERROR_FILE_NOT_FOUND : ERROR_ACCESS_DENIED;
|
||||
return FALSE;
|
||||
}
|
||||
pid_t pid = obj->pid;
|
||||
|
||||
if (lpProcessInformation) {
|
||||
lpProcessInformation->hProcess = processes::allocProcessHandle(pid);
|
||||
lpProcessInformation->hProcess = wibo::handles().alloc(std::move(obj), 0 /* TODO: access */, 0);
|
||||
lpProcessInformation->hThread = nullptr;
|
||||
lpProcessInformation->dwProcessId = static_cast<DWORD>(pid);
|
||||
lpProcessInformation->dwThreadId = 0;
|
||||
|
@ -88,7 +88,7 @@ BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue);
|
||||
HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
|
||||
LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags,
|
||||
LPDWORD lpThreadId);
|
||||
void WIN_FUNC ExitThread(DWORD dwExitCode);
|
||||
[[noreturn]] void WIN_FUNC ExitThread(DWORD dwExitCode);
|
||||
BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);
|
||||
BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority);
|
||||
int WIN_FUNC GetThreadPriority(HANDLE hThread);
|
||||
|
@ -1,24 +1,23 @@
|
||||
#include "synchapi.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "processes.h"
|
||||
#include "strutil.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
std::u16string makeMutexName(LPCWSTR name) {
|
||||
std::u16string makeU16String(LPCWSTR name) {
|
||||
if (!name) {
|
||||
return {};
|
||||
}
|
||||
@ -43,155 +42,34 @@ void WIN_FUNC Sleep(DWORD dwMilliseconds) {
|
||||
usleep(static_cast<useconds_t>(dwMilliseconds) * 1000);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex mutexRegistryLock;
|
||||
std::unordered_map<std::u16string, MutexObject *> namedMutexes;
|
||||
|
||||
std::mutex eventRegistryLock;
|
||||
std::unordered_map<std::u16string, EventObject *> namedEvents;
|
||||
|
||||
std::mutex semaphoreRegistryLock;
|
||||
std::unordered_map<std::u16string, SemaphoreObject *> namedSemaphores;
|
||||
|
||||
EventObject *eventObjectFromHandle(HANDLE hEvent) {
|
||||
auto data = handles::dataFromHandle(hEvent, false);
|
||||
if (data.type != handles::TYPE_EVENT || data.ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<EventObject *>(data.ptr);
|
||||
}
|
||||
|
||||
SemaphoreObject *semaphoreObjectFromHandle(HANDLE hSemaphore) {
|
||||
auto data = handles::dataFromHandle(hSemaphore, false);
|
||||
if (data.type != handles::TYPE_SEMAPHORE || data.ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<SemaphoreObject *>(data.ptr);
|
||||
}
|
||||
|
||||
MutexObject *mutexObjectFromHandle(HANDLE hMutex) {
|
||||
auto data = handles::dataFromHandle(hMutex, false);
|
||||
if (data.type != handles::TYPE_MUTEX || data.ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<MutexObject *>(data.ptr);
|
||||
}
|
||||
|
||||
bool setEventSignaledState(HANDLE hEvent, bool signaled) {
|
||||
EventObject *obj = eventObjectFromHandle(hEvent);
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->signaled = signaled;
|
||||
if (signaled) {
|
||||
if (obj->manualReset) {
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
} else {
|
||||
pthread_cond_signal(&obj->cond);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void releaseMutexObject(MutexObject *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutexRegistryLock);
|
||||
obj->refCount--;
|
||||
if (obj->refCount == 0) {
|
||||
if (!obj->name.empty()) {
|
||||
namedMutexes.erase(obj->name);
|
||||
}
|
||||
pthread_mutex_destroy(&obj->mutex);
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
|
||||
void releaseEventObject(EventObject *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(eventRegistryLock);
|
||||
obj->refCount--;
|
||||
if (obj->refCount == 0) {
|
||||
if (!obj->name.empty()) {
|
||||
namedEvents.erase(obj->name);
|
||||
}
|
||||
pthread_cond_destroy(&obj->cond);
|
||||
pthread_mutex_destroy(&obj->mutex);
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
|
||||
void releaseSemaphoreObject(SemaphoreObject *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(semaphoreRegistryLock);
|
||||
obj->refCount--;
|
||||
if (obj->refCount == 0) {
|
||||
if (!obj->name.empty()) {
|
||||
namedSemaphores.erase(obj->name);
|
||||
}
|
||||
pthread_cond_destroy(&obj->cond);
|
||||
pthread_mutex_destroy(&obj->mutex);
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName) {
|
||||
std::string nameLog;
|
||||
if (lpName) {
|
||||
nameLog = wideStringToString(reinterpret_cast<const uint16_t *>(lpName));
|
||||
} else {
|
||||
nameLog = "<unnamed>";
|
||||
DEBUG_LOG("CreateMutexW(%p, %d, %ls)\n", lpMutexAttributes, static_cast<int>(bInitialOwner),
|
||||
wideStringToString(lpName).c_str());
|
||||
std::u16string name = makeU16String(lpName);
|
||||
const uint32_t grantedAccess = MUTEX_ALL_ACCESS;
|
||||
uint32_t handleFlags = 0;
|
||||
if (lpMutexAttributes && lpMutexAttributes->bInheritHandle) {
|
||||
handleFlags |= HANDLE_FLAG_INHERIT;
|
||||
}
|
||||
DEBUG_LOG("CreateMutexW(%p, %d, %s)\n", lpMutexAttributes, bInitialOwner, nameLog.c_str());
|
||||
(void)lpMutexAttributes;
|
||||
|
||||
std::u16string name = makeMutexName(lpName);
|
||||
MutexObject *obj = nullptr;
|
||||
bool alreadyExists = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutexRegistryLock);
|
||||
if (!name.empty()) {
|
||||
auto it = namedMutexes.find(name);
|
||||
if (it != namedMutexes.end()) {
|
||||
obj = it->second;
|
||||
obj->refCount++;
|
||||
alreadyExists = true;
|
||||
}
|
||||
}
|
||||
if (!obj) {
|
||||
obj = new MutexObject();
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&obj->mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
obj->name = name;
|
||||
if (!name.empty()) {
|
||||
namedMutexes[name] = obj;
|
||||
}
|
||||
auto [mu, created] = wibo::g_namespace.getOrCreate(name, [&]() {
|
||||
auto *mu = new MutexObject();
|
||||
if (bInitialOwner) {
|
||||
std::lock_guard lk(mu->m);
|
||||
mu->owner = pthread_self();
|
||||
mu->ownerValid = true;
|
||||
mu->recursionCount = 1;
|
||||
mu->signaled.store(false, std::memory_order_release);
|
||||
}
|
||||
return mu;
|
||||
});
|
||||
if (!mu) {
|
||||
// Name exists but isn't a mutex
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!alreadyExists && bInitialOwner) {
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
obj->owner = pthread_self();
|
||||
obj->ownerValid = true;
|
||||
obj->recursionCount = 1;
|
||||
}
|
||||
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_MUTEX, obj, 0});
|
||||
wibo::lastError = alreadyExists ? ERROR_ALREADY_EXISTS : ERROR_SUCCESS;
|
||||
return handle;
|
||||
HANDLE h = wibo::handles().alloc(std::move(mu), grantedAccess, handleFlags);
|
||||
wibo::lastError = created ? ERROR_SUCCESS : ERROR_ALREADY_EXISTS;
|
||||
return h;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName) {
|
||||
@ -204,67 +82,58 @@ HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInit
|
||||
|
||||
BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex) {
|
||||
DEBUG_LOG("ReleaseMutex(%p)\n", hMutex);
|
||||
MutexObject *obj = mutexObjectFromHandle(hMutex);
|
||||
if (!obj) {
|
||||
auto mu = wibo::handles().getAs<MutexObject>(hMutex);
|
||||
if (!mu) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
pthread_t self = pthread_self();
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
if (!obj->ownerValid || !pthread_equal(obj->owner, self) || obj->recursionCount == 0) {
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::lastError = ERROR_NOT_OWNER;
|
||||
return FALSE;
|
||||
const pthread_t self = pthread_self();
|
||||
bool notify = false;
|
||||
{
|
||||
std::lock_guard lk(mu->m);
|
||||
if (!mu->ownerValid || !pthread_equal(mu->owner, self) || mu->recursionCount == 0) {
|
||||
wibo::lastError = ERROR_NOT_OWNER;
|
||||
return FALSE;
|
||||
}
|
||||
if (--mu->recursionCount == 0) {
|
||||
mu->ownerValid = false;
|
||||
mu->signaled.store(true, std::memory_order_release);
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
obj->recursionCount--;
|
||||
if (obj->recursionCount == 0) {
|
||||
obj->ownerValid = false;
|
||||
if (notify) {
|
||||
mu->cv.notify_one();
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
|
||||
LPCWSTR lpName) {
|
||||
std::string nameLog;
|
||||
if (lpName) {
|
||||
nameLog = wideStringToString(reinterpret_cast<const uint16_t *>(lpName));
|
||||
} else {
|
||||
nameLog = "<unnamed>";
|
||||
DEBUG_LOG("CreateEventW(%p, %d, %d, %ls)\n", lpEventAttributes, static_cast<int>(bManualReset),
|
||||
static_cast<int>(bInitialState), wideStringToString(lpName).c_str());
|
||||
std::u16string name = makeU16String(lpName);
|
||||
const uint32_t grantedAccess = EVENT_ALL_ACCESS;
|
||||
uint32_t handleFlags = 0;
|
||||
if (lpEventAttributes && lpEventAttributes->bInheritHandle) {
|
||||
handleFlags |= HANDLE_FLAG_INHERIT;
|
||||
}
|
||||
DEBUG_LOG("CreateEventW(%p, %d, %d, %s)\n", lpEventAttributes, bManualReset, bInitialState, nameLog.c_str());
|
||||
(void)lpEventAttributes;
|
||||
|
||||
std::u16string name = makeMutexName(lpName);
|
||||
EventObject *obj = nullptr;
|
||||
bool alreadyExists = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(eventRegistryLock);
|
||||
if (!name.empty()) {
|
||||
auto it = namedEvents.find(name);
|
||||
if (it != namedEvents.end()) {
|
||||
obj = it->second;
|
||||
obj->refCount++;
|
||||
alreadyExists = true;
|
||||
}
|
||||
}
|
||||
if (!obj) {
|
||||
obj = new EventObject();
|
||||
pthread_mutex_init(&obj->mutex, nullptr);
|
||||
pthread_cond_init(&obj->cond, nullptr);
|
||||
obj->manualReset = bManualReset;
|
||||
obj->signaled = bInitialState;
|
||||
obj->name = name;
|
||||
if (!name.empty()) {
|
||||
namedEvents[name] = obj;
|
||||
}
|
||||
auto [ev, alreadyExists] = wibo::g_namespace.getOrCreate(name, [&]() {
|
||||
auto e = new EventObject(!!bManualReset);
|
||||
if (bInitialState) {
|
||||
std::lock_guard lk(e->m);
|
||||
e->signaled.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
return e;
|
||||
});
|
||||
if (!ev) {
|
||||
// Name exists but isn't an event
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_EVENT, obj, 0});
|
||||
HANDLE h = wibo::handles().alloc(std::move(ev), grantedAccess, handleFlags);
|
||||
wibo::lastError = alreadyExists ? ERROR_ALREADY_EXISTS : ERROR_SUCCESS;
|
||||
return handle;
|
||||
return h;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
|
||||
@ -278,43 +147,28 @@ HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManu
|
||||
|
||||
HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
|
||||
LPCWSTR lpName) {
|
||||
DEBUG_LOG("CreateSemaphoreW(%p, %ld, %ld, %ls)\n", lpSemaphoreAttributes, static_cast<long>(lInitialCount),
|
||||
static_cast<long>(lMaximumCount), lpName ? reinterpret_cast<const wchar_t *>(lpName) : L"<null>");
|
||||
(void)lpSemaphoreAttributes;
|
||||
|
||||
std::u16string name = makeMutexName(lpName);
|
||||
SemaphoreObject *obj = nullptr;
|
||||
bool alreadyExists = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(semaphoreRegistryLock);
|
||||
if (!name.empty()) {
|
||||
auto it = namedSemaphores.find(name);
|
||||
if (it != namedSemaphores.end()) {
|
||||
obj = it->second;
|
||||
obj->refCount++;
|
||||
alreadyExists = true;
|
||||
}
|
||||
}
|
||||
if (!obj) {
|
||||
if (lMaximumCount <= 0 || lInitialCount < 0 || lInitialCount > lMaximumCount) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
obj = new SemaphoreObject();
|
||||
pthread_mutex_init(&obj->mutex, nullptr);
|
||||
pthread_cond_init(&obj->cond, nullptr);
|
||||
obj->count = lInitialCount;
|
||||
obj->maxCount = lMaximumCount;
|
||||
obj->name = name;
|
||||
if (!name.empty()) {
|
||||
namedSemaphores[name] = obj;
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("CreateSemaphoreW(%p, %ld, %ld, %ls)\n", lpSemaphoreAttributes, lInitialCount, lMaximumCount,
|
||||
wideStringToString(lpName).c_str());
|
||||
auto name = makeU16String(lpName);
|
||||
const uint32_t granted = SEMAPHORE_ALL_ACCESS;
|
||||
uint32_t hflags = 0;
|
||||
if (lpSemaphoreAttributes && lpSemaphoreAttributes->bInheritHandle) {
|
||||
hflags |= HANDLE_FLAG_INHERIT;
|
||||
}
|
||||
|
||||
HANDLE handle = handles::allocDataHandle({handles::TYPE_SEMAPHORE, obj, 0});
|
||||
wibo::lastError = alreadyExists ? ERROR_ALREADY_EXISTS : ERROR_SUCCESS;
|
||||
return handle;
|
||||
auto [sem, created] = wibo::g_namespace.getOrCreate(name, [&]() -> SemaphoreObject * {
|
||||
if (lMaximumCount <= 0 || lInitialCount < 0 || lInitialCount > lMaximumCount) {
|
||||
return nullptr;
|
||||
}
|
||||
return new SemaphoreObject(lInitialCount, lMaximumCount);
|
||||
});
|
||||
if (!sem) {
|
||||
// Name exists but isn't an event
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
HANDLE h = wibo::handles().alloc(std::move(sem), granted, hflags);
|
||||
wibo::lastError = created ? ERROR_SUCCESS : ERROR_ALREADY_EXISTS;
|
||||
return h;
|
||||
}
|
||||
|
||||
HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount,
|
||||
@ -327,169 +181,165 @@ HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LO
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount) {
|
||||
DEBUG_LOG("ReleaseSemaphore(%p, %ld, %p)\n", hSemaphore, static_cast<long>(lReleaseCount), lpPreviousCount);
|
||||
DEBUG_LOG("ReleaseSemaphore(%p, %ld, %p)\n", hSemaphore, lReleaseCount, lpPreviousCount);
|
||||
if (lReleaseCount <= 0) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
SemaphoreObject *obj = semaphoreObjectFromHandle(hSemaphore);
|
||||
if (!obj) {
|
||||
auto sem = wibo::handles().getAs<SemaphoreObject>(hSemaphore);
|
||||
if (!sem) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
|
||||
LONG prev = 0;
|
||||
{
|
||||
std::lock_guard lk(sem->m);
|
||||
if (lpPreviousCount) {
|
||||
prev = sem->count;
|
||||
}
|
||||
if (sem->count > sem->maxCount - lReleaseCount) {
|
||||
wibo::lastError = ERROR_TOO_MANY_POSTS;
|
||||
return FALSE;
|
||||
}
|
||||
sem->count += lReleaseCount;
|
||||
sem->signaled.store(sem->count > 0, std::memory_order_release);
|
||||
}
|
||||
for (LONG i = 0; i < lReleaseCount; ++i) {
|
||||
sem->cv.notify_one();
|
||||
}
|
||||
|
||||
if (lpPreviousCount) {
|
||||
*lpPreviousCount = obj->count;
|
||||
*lpPreviousCount = prev;
|
||||
}
|
||||
if (lReleaseCount > obj->maxCount - obj->count) {
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
obj->count += lReleaseCount;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
pthread_cond_broadcast(&obj->cond);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC SetEvent(HANDLE hEvent) {
|
||||
DEBUG_LOG("SetEvent(%p)\n", hEvent);
|
||||
if (!setEventSignaledState(hEvent, true)) {
|
||||
auto ev = wibo::handles().getAs<EventObject>(hEvent);
|
||||
if (!ev) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
ev->set();
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC ResetEvent(HANDLE hEvent) {
|
||||
DEBUG_LOG("ResetEvent(%p)\n", hEvent);
|
||||
if (!setEventSignaledState(hEvent, false)) {
|
||||
auto ev = wibo::handles().getAs<EventObject>(hEvent);
|
||||
if (!ev) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
ev->reset();
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
DEBUG_LOG("WaitForSingleObject(%p, %u)\n", hHandle, dwMilliseconds);
|
||||
handles::Data data = handles::dataFromHandle(hHandle, false);
|
||||
switch (data.type) {
|
||||
case handles::TYPE_PROCESS: {
|
||||
if (dwMilliseconds != INFINITE) {
|
||||
DEBUG_LOG("WaitForSingleObject: timeout for process not supported\n");
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
auto *process = reinterpret_cast<processes::Process *>(data.ptr);
|
||||
int status = 0;
|
||||
for (;;) {
|
||||
if (waitpid(process->pid, &status, 0) == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (errno == ECHILD && process->terminationRequested) {
|
||||
process->exitCode = process->forcedExitCode;
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG("WaitForSingleObject: waitpid(%d) failed: %s\n", process->pid, strerror(errno));
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (process->terminationRequested) {
|
||||
process->exitCode = process->forcedExitCode;
|
||||
} else if (WIFEXITED(status)) {
|
||||
process->exitCode = static_cast<DWORD>(WEXITSTATUS(status));
|
||||
HandleMeta meta{};
|
||||
Pin<> obj = wibo::handles().get(hHandle, &meta);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
DEBUG_LOG("-> ERROR_INVALID_HANDLE\n");
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
#ifdef CHECK_ACCESS
|
||||
if ((meta.grantedAccess & SYNCHRONIZE) == 0) {
|
||||
wibo::lastError = ERROR_ACCESS_DENIED;
|
||||
DEBUG_LOG("!!! DENIED: 0x%x\n", meta.grantedAccess);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto doWait = [&](auto &lk, auto &cv, auto pred) -> bool {
|
||||
if (dwMilliseconds == INFINITE) {
|
||||
cv.wait(lk, pred);
|
||||
return true;
|
||||
} else {
|
||||
DEBUG_LOG("WaitForSingleObject: child process exited abnormally - returning exit code 1\n");
|
||||
process->exitCode = 1;
|
||||
return cv.wait_for(lk, std::chrono::milliseconds(dwMilliseconds), pred);
|
||||
}
|
||||
};
|
||||
|
||||
switch (obj->type) {
|
||||
case ObjectType::Event: {
|
||||
auto ev = std::move(obj).downcast<EventObject>();
|
||||
std::unique_lock lk(ev->m);
|
||||
bool ok = doWait(lk, ev->cv, [&] { return ev->signaled.load(std::memory_order_acquire); });
|
||||
if (!ok) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
if (!ev->manualReset) {
|
||||
ev->signaled.store(false, std::memory_order_release);
|
||||
}
|
||||
process->terminationRequested = false;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
case handles::TYPE_EVENT: {
|
||||
EventObject *obj = reinterpret_cast<EventObject *>(data.ptr);
|
||||
if (dwMilliseconds != INFINITE) {
|
||||
DEBUG_LOG("WaitForSingleObject: timeout for event not supported\n");
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return WAIT_FAILED;
|
||||
case ObjectType::Semaphore: {
|
||||
auto sem = std::move(obj).downcast<SemaphoreObject>();
|
||||
std::unique_lock lk(sem->m);
|
||||
bool ok = doWait(lk, sem->cv, [&] { return sem->count > 0; });
|
||||
if (!ok) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
while (!obj->signaled) {
|
||||
pthread_cond_wait(&obj->cond, &obj->mutex);
|
||||
--sem->count;
|
||||
if (sem->count == 0) {
|
||||
sem->signaled.store(false, std::memory_order_release);
|
||||
}
|
||||
if (!obj->manualReset) {
|
||||
obj->signaled = false;
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
case handles::TYPE_THREAD: {
|
||||
ThreadObject *obj = reinterpret_cast<ThreadObject *>(data.ptr);
|
||||
if (dwMilliseconds != INFINITE) {
|
||||
DEBUG_LOG("WaitForSingleObject: timeout for thread not supported\n");
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
while (!obj->finished) {
|
||||
pthread_cond_wait(&obj->cond, &obj->mutex);
|
||||
}
|
||||
bool needJoin = !obj->joined && !obj->detached;
|
||||
pthread_t thread = obj->thread;
|
||||
if (needJoin) {
|
||||
obj->joined = true;
|
||||
}
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
if (needJoin) {
|
||||
pthread_join(thread, nullptr);
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
case handles::TYPE_SEMAPHORE: {
|
||||
SemaphoreObject *obj = reinterpret_cast<SemaphoreObject *>(data.ptr);
|
||||
if (dwMilliseconds != INFINITE) {
|
||||
DEBUG_LOG("WaitForSingleObject: timeout for semaphore not supported\n");
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
while (obj->count == 0) {
|
||||
pthread_cond_wait(&obj->cond, &obj->mutex);
|
||||
}
|
||||
obj->count--;
|
||||
pthread_mutex_unlock(&obj->mutex);
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
case handles::TYPE_MUTEX: {
|
||||
MutexObject *obj = reinterpret_cast<MutexObject *>(data.ptr);
|
||||
if (dwMilliseconds != INFINITE) {
|
||||
DEBUG_LOG("WaitForSingleObject: timeout for mutex not supported\n");
|
||||
wibo::lastError = ERROR_NOT_SUPPORTED;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
pthread_mutex_lock(&obj->mutex);
|
||||
case ObjectType::Mutex: {
|
||||
auto mu = std::move(obj).downcast<MutexObject>();
|
||||
pthread_t self = pthread_self();
|
||||
if (obj->ownerValid && pthread_equal(obj->owner, self)) {
|
||||
obj->recursionCount++;
|
||||
} else {
|
||||
obj->owner = self;
|
||||
obj->ownerValid = true;
|
||||
obj->recursionCount = 1;
|
||||
std::unique_lock lk(mu->m);
|
||||
// Recursive acquisition
|
||||
if (mu->ownerValid && pthread_equal(mu->owner, self)) {
|
||||
++mu->recursionCount;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return WAIT_OBJECT_0;
|
||||
bool ok = doWait(lk, mu->cv, [&] { return !mu->ownerValid || mu->abandoned; });
|
||||
if (!ok) {
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
DWORD ret = WAIT_OBJECT_0;
|
||||
if (std::exchange(mu->abandoned, false)) {
|
||||
// Acquire and report abandoned
|
||||
ret = WAIT_ABANDONED;
|
||||
}
|
||||
mu->owner = self;
|
||||
mu->ownerValid = true;
|
||||
mu->recursionCount = 1;
|
||||
mu->signaled.store(false, std::memory_order_release);
|
||||
return ret;
|
||||
}
|
||||
case ObjectType::Thread: {
|
||||
auto th = std::move(obj).downcast<ThreadObject>();
|
||||
pthread_t self = pthread_self();
|
||||
std::unique_lock lk(th->m);
|
||||
if (pthread_equal(th->thread, self)) {
|
||||
// Cannot wait on self
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
bool ok = doWait(lk, th->cv, [&] { return th->signaled.load(std::memory_order_acquire); });
|
||||
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
|
||||
}
|
||||
case ObjectType::Process: {
|
||||
auto po = std::move(obj).downcast<ProcessObject>();
|
||||
std::unique_lock lk(po->m);
|
||||
if (po->pidfd == -1) {
|
||||
// Cannot wait on self
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
bool ok = doWait(lk, po->cv, [&] { return po->signaled.load(std::memory_order_acquire); });
|
||||
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
|
||||
}
|
||||
default:
|
||||
DEBUG_LOG("WaitForSingleObject: unsupported handle type %d\n", data.type);
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
@ -594,18 +444,4 @@ BOOLEAN WIN_FUNC TryAcquireSRWLockExclusive(PSRWLOCK SRWLock) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void resetOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!ov || !ov->hEvent) {
|
||||
return;
|
||||
}
|
||||
setEventSignaledState(ov->hEvent, false);
|
||||
}
|
||||
|
||||
void signalOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!ov || !ov->hEvent) {
|
||||
return;
|
||||
}
|
||||
setEventSignaledState(ov->hEvent, true);
|
||||
}
|
||||
|
||||
} // namespace kernel32
|
||||
|
@ -168,7 +168,7 @@ namespace kernel32 {
|
||||
UINT WIN_FUNC SetHandleCount(UINT uNumber) {
|
||||
DEBUG_LOG("SetHandleCount(%u)\n", uNumber);
|
||||
(void)uNumber;
|
||||
return handles::MAX_HANDLES;
|
||||
return (1u << 17) - 1;
|
||||
}
|
||||
|
||||
DWORD WIN_FUNC FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer,
|
||||
|
@ -74,10 +74,10 @@ BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumb
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FILE *fp = files::fpFromHandle(hConsoleOutput);
|
||||
if (fp == stdout || fp == stderr) {
|
||||
auto file = wibo::handles().getAs<FileObject>(hConsoleOutput);
|
||||
if (file->fd == STDOUT_FILENO || file->fd == STDERR_FILENO) {
|
||||
auto str = wideStringToString(static_cast<const uint16_t *>(lpBuffer), nNumberOfCharsToWrite);
|
||||
fprintf(fp, "%s", str.c_str());
|
||||
dprintf(file->fd, "%s", str.c_str());
|
||||
if (lpNumberOfCharsWritten) {
|
||||
*lpNumberOfCharsWritten = nNumberOfCharsToWrite;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "wow64apiset.h"
|
||||
#include "common.h"
|
||||
#include "dll/kernel32/internal.h"
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
|
||||
@ -29,14 +30,14 @@ BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
|
||||
}
|
||||
|
||||
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(hProcess);
|
||||
bool isPseudoHandle = rawHandle == static_cast<uintptr_t>(-1);
|
||||
bool isPseudoHandle = rawHandle == kPseudoCurrentProcessHandleValue;
|
||||
if (!isPseudoHandle) {
|
||||
if (!hProcess) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
auto data = handles::dataFromHandle(hProcess, false);
|
||||
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
|
||||
auto obj = wibo::handles().getAs<ProcessObject>(hProcess);
|
||||
if (!obj) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "dll/kernel32/internal.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
@ -2687,7 +2688,7 @@ namespace msvcrt {
|
||||
argStorage.emplace_back(wideStringToString(*cursor));
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(command, false);
|
||||
auto resolved = wibo::resolveExecutable(command, false);
|
||||
if (!resolved) {
|
||||
errno = ENOENT;
|
||||
DEBUG_LOG("-> failed to resolve executable for %s\n", command.c_str());
|
||||
@ -2695,31 +2696,22 @@ namespace msvcrt {
|
||||
}
|
||||
DEBUG_LOG("-> resolved to %s\n", resolved->c_str());
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnWithArgv(*resolved, argStorage, &pid);
|
||||
Pin<ProcessObject> po;
|
||||
int spawnResult = wibo::spawnWithArgv(*resolved, argStorage, po);
|
||||
if (spawnResult != 0) {
|
||||
errno = spawnResult;
|
||||
DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_LOG("-> spawned pid %d\n", pid);
|
||||
DEBUG_LOG("-> spawned pid %d\n", po->pid);
|
||||
|
||||
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;
|
||||
std::unique_lock lk(po->m);
|
||||
po->cv.wait(lk, [&] { return po->signaled.load(); });
|
||||
return static_cast<intptr_t>(po->exitCode);
|
||||
}
|
||||
|
||||
if (mode == P_DETACH) {
|
||||
@ -2727,7 +2719,7 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
// _P_NOWAIT and unknown flags: return process id
|
||||
return static_cast<intptr_t>(pid);
|
||||
return static_cast<intptr_t>(po->pid);
|
||||
}
|
||||
|
||||
intptr_t WIN_ENTRY _spawnvp(int mode, const char *cmdname, const char * const *argv) {
|
||||
@ -2744,7 +2736,7 @@ namespace msvcrt {
|
||||
argStorage.emplace_back(*cursor);
|
||||
}
|
||||
|
||||
auto resolved = processes::resolveExecutable(command, false);
|
||||
auto resolved = wibo::resolveExecutable(command, false);
|
||||
if (!resolved) {
|
||||
errno = ENOENT;
|
||||
DEBUG_LOG("-> failed to resolve executable for %s\n", command.c_str());
|
||||
@ -2752,37 +2744,29 @@ namespace msvcrt {
|
||||
}
|
||||
DEBUG_LOG("-> resolved to %s\n", resolved->c_str());
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = processes::spawnWithArgv(*resolved, argStorage, &pid);
|
||||
Pin<ProcessObject> po;
|
||||
int spawnResult = wibo::spawnWithArgv(*resolved, argStorage, po);
|
||||
if (spawnResult != 0) {
|
||||
errno = spawnResult;
|
||||
DEBUG_LOG("-> spawnWithArgv failed: %d\n", spawnResult);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_LOG("-> spawned pid %d\n", pid);
|
||||
DEBUG_LOG("-> spawned pid %d\n", po->pid);
|
||||
|
||||
constexpr int P_WAIT = 0;
|
||||
constexpr int P_DETACH = 2;
|
||||
|
||||
if (mode == P_WAIT) {
|
||||
int status = 0;
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
return static_cast<intptr_t>(WEXITSTATUS(status));
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
errno = EINTR;
|
||||
}
|
||||
return -1;
|
||||
std::unique_lock lk(po->m);
|
||||
po->cv.wait(lk, [&] { return po->signaled.load(); });
|
||||
return static_cast<intptr_t>(po->exitCode);
|
||||
}
|
||||
|
||||
if (mode == P_DETACH) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<intptr_t>(pid);
|
||||
return static_cast<intptr_t>(po->pid);
|
||||
}
|
||||
|
||||
int WIN_ENTRY _wunlink(const uint16_t *filename){
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "common.h"
|
||||
#include "dll/kernel32/internal.h"
|
||||
#include "errors.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
@ -71,10 +72,10 @@ using PRTL_OSVERSIONINFOEXW = RTL_OSVERSIONINFOEXW *;
|
||||
constexpr ULONG kOsMajorVersion = 6;
|
||||
constexpr ULONG kOsMinorVersion = 2;
|
||||
constexpr ULONG kOsBuildNumber = 0;
|
||||
constexpr ULONG kOsPlatformId = 2; // VER_PLATFORM_WIN32_NT
|
||||
constexpr ULONG kOsPlatformId = 2; // VER_PLATFORM_WIN32_NT
|
||||
constexpr BYTE kProductTypeWorkstation = 1; // VER_NT_WORKSTATION
|
||||
|
||||
static bool resolveProcessDetails(HANDLE processHandle, ProcessHandleDetails &details) {
|
||||
bool resolveProcessDetails(HANDLE processHandle, ProcessHandleDetails &details) {
|
||||
uintptr_t rawHandle = reinterpret_cast<uintptr_t>(processHandle);
|
||||
if (rawHandle == static_cast<uintptr_t>(-1)) {
|
||||
details.pid = getpid();
|
||||
@ -84,20 +85,19 @@ static bool resolveProcessDetails(HANDLE processHandle, ProcessHandleDetails &de
|
||||
return true;
|
||||
}
|
||||
|
||||
auto data = handles::dataFromHandle(processHandle, false);
|
||||
if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) {
|
||||
auto po = wibo::handles().getAs<ProcessObject>(processHandle);
|
||||
if (!po) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *process = reinterpret_cast<processes::Process *>(data.ptr);
|
||||
details.pid = process->pid;
|
||||
details.exitCode = process->exitCode;
|
||||
details.isCurrentProcess = (process->pid == getpid());
|
||||
details.pid = po->pid;
|
||||
details.exitCode = po->exitCode;
|
||||
details.isCurrentProcess = po->pid == getpid();
|
||||
details.peb = details.isCurrentProcess ? wibo::processPeb : nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string windowsImagePathFor(const ProcessHandleDetails &details) {
|
||||
std::string windowsImagePathFor(const ProcessHandleDetails &details) {
|
||||
if (details.isCurrentProcess && !wibo::guestExecutablePath.empty()) {
|
||||
return files::pathToWindows(files::canonicalPath(wibo::guestExecutablePath));
|
||||
}
|
||||
@ -134,15 +134,15 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto file = files::fileHandleFromHandle(FileHandle);
|
||||
if (!file || !file->fp) {
|
||||
auto file = wibo::handles().getAs<FileObject>(FileHandle);
|
||||
if (!file || !file->valid()) {
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
IoStatusBlock->Status = STATUS_INVALID_HANDLE;
|
||||
IoStatusBlock->Information = 0;
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0;
|
||||
bool handleOverlapped = file->overlapped;
|
||||
std::optional<uint64_t> offset;
|
||||
bool updateFilePointer = !handleOverlapped;
|
||||
if (ByteOffset) {
|
||||
@ -155,7 +155,7 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
||||
kernel32::ResetEvent(Event);
|
||||
}
|
||||
|
||||
auto io = files::read(file, Buffer, Length, offset, updateFilePointer);
|
||||
auto io = files::read(file.get(), Buffer, Length, offset, updateFilePointer);
|
||||
DWORD winError = ERROR_SUCCESS;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
@ -286,10 +286,10 @@ NTSTATUS WIN_FUNC RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation) {
|
||||
}
|
||||
|
||||
NTSTATUS WIN_FUNC NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass,
|
||||
PVOID ProcessInformation, ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength) {
|
||||
PVOID ProcessInformation, ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength) {
|
||||
DEBUG_LOG("NtQueryInformationProcess(%p, %u, %p, %u, %p) ", ProcessHandle, ProcessInformationClass,
|
||||
ProcessInformation, ProcessInformationLength, ReturnLength);
|
||||
ProcessInformation, ProcessInformationLength, ReturnLength);
|
||||
if (!ProcessInformation) {
|
||||
DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
@ -371,7 +371,8 @@ NTSTATUS WIN_FUNC NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLA
|
||||
}
|
||||
|
||||
auto *unicode = reinterpret_cast<UNICODE_STRING *>(ProcessInformation);
|
||||
auto *buffer = reinterpret_cast<uint16_t *>(reinterpret_cast<uint8_t *>(ProcessInformation) + sizeof(UNICODE_STRING));
|
||||
auto *buffer =
|
||||
reinterpret_cast<uint16_t *>(reinterpret_cast<uint8_t *>(ProcessInformation) + sizeof(UNICODE_STRING));
|
||||
std::memcpy(buffer, widePath.data(), stringBytes);
|
||||
size_t characterCount = widePath.empty() ? 0 : widePath.size() - 1;
|
||||
unicode->Length = static_cast<unsigned short>(characterCount * sizeof(uint16_t));
|
||||
|
2
errors.h
2
errors.h
@ -37,6 +37,8 @@
|
||||
#define ERROR_DLL_INIT_FAILED 1114
|
||||
#define ERROR_ALREADY_EXISTS 183
|
||||
#define ERROR_NOT_OWNER 288
|
||||
#define ERROR_TOO_MANY_POSTS 298
|
||||
#define ERROR_SEM_TIMEOUT 121
|
||||
|
||||
#define INVALID_SET_FILE_POINTER ((DWORD) - 1)
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE) - 1)
|
||||
|
125
files.cpp
125
files.cpp
@ -6,12 +6,28 @@
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <system_error>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
kernel32::FsObject::~FsObject() {
|
||||
std::lock_guard lk(m);
|
||||
int fd = std::exchange(this->fd, -1);
|
||||
if (fd >= 0 && closeOnDestroy) {
|
||||
close(fd);
|
||||
}
|
||||
if (deletePending && !canonicalPath.empty()) {
|
||||
if (unlink(canonicalPath.c_str()) != 0) {
|
||||
perror("Failed to delete file on close");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace files {
|
||||
|
||||
@ -59,9 +75,9 @@ static std::string toHostPathEntry(const std::string &entry) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
static void *stdinHandle;
|
||||
static void *stdoutHandle;
|
||||
static void *stderrHandle;
|
||||
static HANDLE stdinHandle;
|
||||
static HANDLE stdoutHandle;
|
||||
static HANDLE stderrHandle;
|
||||
|
||||
std::filesystem::path pathFromWindows(const char *inStr) {
|
||||
// Convert to forward slashes
|
||||
@ -126,36 +142,6 @@ std::string pathToWindows(const std::filesystem::path &path) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// FileHandle *fileHandleFromHandle(void *handle) {
|
||||
// handles::Data data = handles::dataFromHandle(handle, false);
|
||||
// if (data.type == handles::TYPE_FILE) {
|
||||
// return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// FILE *fpFromHandle(void *handle, bool pop) {
|
||||
// handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
// if (data.type == handles::TYPE_FILE) {
|
||||
// return reinterpret_cast<FileHandle *>(data.ptr)->fp;
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// void *duplicateFileHandle(FileHandle *source, bool closeOnDestroy) {
|
||||
// if (!source) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// auto *clone = new FileHandle();
|
||||
// clone->fp = source->fp;
|
||||
// clone->fd = source->fd;
|
||||
// clone->desiredAccess = source->desiredAccess;
|
||||
// clone->shareMode = source->shareMode;
|
||||
// clone->flags = source->flags;
|
||||
// clone->closeOnDestroy = closeOnDestroy;
|
||||
// return handles::allocDataHandle({handles::TYPE_FILE, clone, 0});
|
||||
// }
|
||||
|
||||
IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
@ -176,7 +162,7 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
||||
uint8_t *in = static_cast<uint8_t *>(buffer);
|
||||
while (remaining > 0) {
|
||||
size_t chunk = remaining > SSIZE_MAX ? SSIZE_MAX : remaining;
|
||||
ssize_t rc = pread64(file->host_fd, in + total, chunk, pos);
|
||||
ssize_t rc = pread64(file->fd, in + total, chunk, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
@ -198,11 +184,11 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
||||
};
|
||||
|
||||
if (updateFilePointer || !offset.has_value()) {
|
||||
std::unique_lock<std::mutex> lock(file->pos_mu);
|
||||
off64_t pos = offset.value_or(file->file_pos);
|
||||
std::lock_guard lk(file->m);
|
||||
off64_t pos = offset.value_or(file->filePos);
|
||||
doRead(pos);
|
||||
if (updateFilePointer) {
|
||||
file->file_pos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
file->filePos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
}
|
||||
} else {
|
||||
doRead(*offset);
|
||||
@ -211,10 +197,10 @@ IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::opt
|
||||
return result;
|
||||
}
|
||||
|
||||
IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, const std::optional<off64_t> &offset,
|
||||
IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer) {
|
||||
IOResult result{};
|
||||
if (!handle || handle->host_fd < 0) {
|
||||
if (!file || !file->valid()) {
|
||||
result.unixError = EBADF;
|
||||
return result;
|
||||
}
|
||||
@ -225,24 +211,55 @@ IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, cons
|
||||
// Sanity check: if no offset is given, we must update the file pointer
|
||||
assert(offset.has_value() || updateFilePointer);
|
||||
|
||||
if (file->appendOnly || !file->seekable) {
|
||||
std::lock_guard lk(file->m);
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToWrite;
|
||||
const uint8_t *in = static_cast<const uint8_t *>(buffer);
|
||||
while (remaining > 0) {
|
||||
size_t chunk = remaining > SSIZE_MAX ? SSIZE_MAX : remaining;
|
||||
ssize_t rc = ::write(file->fd, in + total, chunk);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.unixError = errno ? errno : EIO;
|
||||
break;
|
||||
}
|
||||
if (rc == 0) {
|
||||
break;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
if (updateFilePointer) {
|
||||
off64_t pos = file->seekable ? lseek64(file->fd, 0, SEEK_CUR) : 0;
|
||||
if (pos >= 0) {
|
||||
file->filePos = pos;
|
||||
} else if (result.unixError == 0) {
|
||||
result.unixError = errno ? errno : EIO;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto doWrite = [&](off64_t pos) {
|
||||
size_t total = 0;
|
||||
size_t remaining = bytesToWrite;
|
||||
const uint8_t *in = static_cast<const uint8_t *>(buffer);
|
||||
while (remaining > 0) {
|
||||
size_t chunk = remaining > SSIZE_MAX ? SSIZE_MAX : remaining;
|
||||
ssize_t rc = pwrite64(handle->host_fd, in + total, chunk, pos);
|
||||
ssize_t rc = pwrite64(file->fd, in + total, chunk, pos);
|
||||
if (rc == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
result.bytesTransferred = total;
|
||||
result.unixError = errno ? errno : EIO;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (rc == 0) {
|
||||
result.bytesTransferred = total;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
total += static_cast<size_t>(rc);
|
||||
remaining -= static_cast<size_t>(rc);
|
||||
@ -252,11 +269,11 @@ IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, cons
|
||||
};
|
||||
|
||||
if (updateFilePointer || !offset.has_value()) {
|
||||
std::unique_lock<std::mutex> lock(handle->pos_mu);
|
||||
const off64_t pos = handle->file_pos;
|
||||
std::lock_guard lk(file->m);
|
||||
const off64_t pos = offset.value_or(file->filePos);
|
||||
doWrite(pos);
|
||||
if (updateFilePointer) {
|
||||
handle->file_pos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
file->filePos = pos + static_cast<off64_t>(result.bytesTransferred);
|
||||
}
|
||||
} else {
|
||||
doWrite(*offset);
|
||||
@ -297,9 +314,15 @@ BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle) {
|
||||
|
||||
void init() {
|
||||
auto &handles = wibo::handles();
|
||||
stdinHandle = handles.create(new FileObject(STDIN_FILENO, false), FILE_GENERIC_READ, 0);
|
||||
stdoutHandle = handles.create(new FileObject(STDOUT_FILENO, false), FILE_GENERIC_WRITE, 0);
|
||||
stderrHandle = handles.create(new FileObject(STDERR_FILENO, false), FILE_GENERIC_WRITE, 0);
|
||||
auto stdinObject = make_pin<FileObject>(STDIN_FILENO);
|
||||
stdinObject->closeOnDestroy = false;
|
||||
stdinHandle = handles.alloc(std::move(stdinObject), FILE_GENERIC_READ, 0);
|
||||
auto stdoutObject = make_pin<FileObject>(STDOUT_FILENO);
|
||||
stdoutObject->closeOnDestroy = false;
|
||||
stdoutHandle = handles.alloc(std::move(stdoutObject), FILE_GENERIC_WRITE, 0);
|
||||
auto stderrObject = make_pin<FileObject>(STDERR_FILENO);
|
||||
stderrObject->closeOnDestroy = false;
|
||||
stderrHandle = handles.alloc(std::move(stderrObject), FILE_GENERIC_WRITE, 0);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory,
|
||||
|
62
files.h
62
files.h
@ -1,55 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "handles.h"
|
||||
#include "dll/kernel32/internal.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
using kernel32::FileObject;
|
||||
|
||||
namespace files {
|
||||
struct FileObject : ObjectHeader {
|
||||
int host_fd = -1; // use pread/pwrite
|
||||
std::mutex pos_mu; // sync for file_pos w/o overlapped
|
||||
off64_t file_pos = 0; // shared across duplicated handles
|
||||
bool overlapped = false; // set from FILE_FLAG_OVERLAPPED
|
||||
|
||||
explicit FileObject(int fd, bool ov)
|
||||
: ObjectHeader(ObjectType::File), host_fd(fd), overlapped(ov) {}
|
||||
virtual ~FileObject() {
|
||||
if (host_fd != -1) {
|
||||
close(host_fd);
|
||||
host_fd = -1;
|
||||
}
|
||||
}
|
||||
struct IOResult {
|
||||
size_t bytesTransferred = 0;
|
||||
int unixError = 0;
|
||||
bool reachedEnd = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool valid() const { return host_fd >= 0; }
|
||||
};
|
||||
void init();
|
||||
std::filesystem::path pathFromWindows(const char *inStr);
|
||||
std::string pathToWindows(const std::filesystem::path &path);
|
||||
IOResult read(FileObject *file, void *buffer, size_t bytesToRead, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer);
|
||||
IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const std::optional<off64_t> &offset,
|
||||
bool updateFilePointer);
|
||||
HANDLE getStdHandle(DWORD nStdHandle);
|
||||
BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle);
|
||||
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);
|
||||
|
||||
struct IOResult {
|
||||
size_t bytesTransferred = 0;
|
||||
int unixError = 0;
|
||||
bool reachedEnd = false;
|
||||
};
|
||||
|
||||
std::filesystem::path pathFromWindows(const char *inStr);
|
||||
std::string pathToWindows(const std::filesystem::path &path);
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess = 0, unsigned int shareMode = 0, unsigned int flags = 0, bool closeOnDestroy = true);
|
||||
// void *duplicateFileHandle(FileHandle *handle, bool closeOnDestroy);
|
||||
FILE *fpFromHandle(void *handle, bool pop = false);
|
||||
// FileHandle *fileHandleFromHandle(void *handle);
|
||||
IOResult read(FileObject *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
HANDLE getStdHandle(DWORD nStdHandle);
|
||||
BOOL setStdHandle(DWORD nStdHandle, HANDLE 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);
|
||||
} // namespace files
|
||||
} // namespace files
|
||||
|
||||
inline bool endsWith(const std::string &str, const std::string &suffix) {
|
||||
return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
|
223
handles.cpp
223
handles.cpp
@ -1,121 +1,144 @@
|
||||
#include "handles.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kIndexBits = 17;
|
||||
constexpr uint32_t kIndexMask = (1u << kIndexBits) - 1; // 0x1FFFF
|
||||
constexpr uint32_t kGenerationMask = (1u << 15) - 1; // 0x7FFF
|
||||
constexpr unsigned kGenerationShift = kIndexBits; // 17
|
||||
constexpr uint32_t kHandleAlignShift = 2;
|
||||
// Max index that still yields HANDLE < 0x10000 with (index + 1) << 2
|
||||
constexpr uint32_t kCompatMaxIndex = (0xFFFFu >> kHandleAlignShift) - 1;
|
||||
// Delay reuse of small handles to avoid accidental stale aliasing
|
||||
constexpr uint32_t kQuarantineLen = 64;
|
||||
|
||||
inline uint32_t indexOf(HANDLE h) { return reinterpret_cast<uint32_t>(h) & kIndexMask; }
|
||||
inline uint32_t generationOf(HANDLE h) { return (reinterpret_cast<uint32_t>(h) >> kGenerationShift) & kGenerationMask; }
|
||||
inline HANDLE makeHandle(uint32_t index, uint32_t gen) {
|
||||
return reinterpret_cast<HANDLE>((gen << kGenerationShift) | index);
|
||||
inline uint32_t indexOf(HANDLE h) {
|
||||
uint32_t v = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h));
|
||||
if (v == 0 || (v & ((1U << kHandleAlignShift) - 1)) != 0) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return (v >> kHandleAlignShift) - 1;
|
||||
}
|
||||
|
||||
inline HANDLE makeHandle(uint32_t index) {
|
||||
uint32_t v = (index + 1) << kHandleAlignShift;
|
||||
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(v));
|
||||
}
|
||||
|
||||
inline bool isPseudo(HANDLE h) { return reinterpret_cast<int32_t>(h) < 0; }
|
||||
|
||||
} // namespace
|
||||
|
||||
HANDLE HandleTable::create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags) {
|
||||
std::unique_lock lk(mu_);
|
||||
HANDLE Handles::alloc(Pin<> obj, uint32_t grantedAccess, uint32_t flags) {
|
||||
std::unique_lock lk(m);
|
||||
|
||||
// Attempt to, in order:
|
||||
// 1) use a fresh index in the compat range (0..kCompatMaxIndex)
|
||||
// 2) reuse a recently-freed index in the compat range
|
||||
// 3) reuse a recently-freed index above the compat range
|
||||
// 4) use a fresh index above the compat range
|
||||
uint32_t idx;
|
||||
if (!freeList_.empty()) {
|
||||
idx = freeList_.back();
|
||||
freeList_.pop_back();
|
||||
if (nextIndex <= kCompatMaxIndex) {
|
||||
idx = nextIndex++;
|
||||
if (idx >= mSlots.size()) {
|
||||
mSlots.emplace_back();
|
||||
}
|
||||
} else if (!mFreeBelow.empty()) {
|
||||
idx = mFreeBelow.back();
|
||||
mFreeBelow.pop_back();
|
||||
} else if (!mFreeAbove.empty()) {
|
||||
idx = mFreeAbove.back();
|
||||
mFreeAbove.pop_back();
|
||||
} else {
|
||||
idx = static_cast<uint32_t>(slots_.size());
|
||||
slots_.push_back(HandleEntry{});
|
||||
idx = static_cast<uint32_t>(mSlots.size());
|
||||
mSlots.emplace_back();
|
||||
}
|
||||
|
||||
auto &e = slots_[idx];
|
||||
|
||||
// Initialize generation if needed
|
||||
// Initialize entry
|
||||
auto &e = mSlots[idx];
|
||||
e.obj = obj.release(); // Transfer ownership
|
||||
e.meta.grantedAccess = grantedAccess;
|
||||
e.meta.flags = flags;
|
||||
e.meta.typeCache = e.obj->type;
|
||||
if (e.meta.generation == 0) {
|
||||
e.meta.generation = 1;
|
||||
}
|
||||
const uint16_t gen = e.meta.generation;
|
||||
|
||||
// Table owns one pointer ref for this entry
|
||||
detail::ref(obj);
|
||||
|
||||
// Initialize entry
|
||||
e.obj = obj;
|
||||
e.meta.grantedAccess = grantedAccess;
|
||||
e.meta.flags = flags;
|
||||
e.meta.typeCache = obj->type;
|
||||
|
||||
HANDLE h = makeHandle(idx, gen);
|
||||
obj->handleCount.fetch_add(1, std::memory_order_acq_rel);
|
||||
HANDLE h = makeHandle(idx);
|
||||
e.obj->handleCount.fetch_add(1, std::memory_order_acq_rel);
|
||||
return h;
|
||||
}
|
||||
|
||||
bool HandleTable::get(HANDLE h, Pin<ObjectHeader> &pinOut, HandleMeta *metaOut) {
|
||||
Pin<> Handles::get(HANDLE h, HandleMeta *metaOut) {
|
||||
if (isPseudo(h)) {
|
||||
return false; // pseudo-handles have no entries
|
||||
return {}; // pseudo-handles have no entries
|
||||
}
|
||||
|
||||
std::shared_lock lk(mu_);
|
||||
std::shared_lock lk(m);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
return false;
|
||||
if (idx >= mSlots.size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
detail::ref(e.obj); // pin under the lock
|
||||
pinOut = Pin<ObjectHeader>::adopt(e.obj); // dtor will deref
|
||||
const auto &e = mSlots[idx];
|
||||
if (!e.obj) {
|
||||
return {};
|
||||
}
|
||||
if (metaOut) {
|
||||
*metaOut = e.meta;
|
||||
}
|
||||
return true;
|
||||
return Pin<>::acquire(e.obj);
|
||||
}
|
||||
|
||||
bool HandleTable::close(HANDLE h) {
|
||||
bool Handles::release(HANDLE h) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
|
||||
std::unique_lock lk(mu_);
|
||||
std::unique_lock lk(m);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
if (idx >= mSlots.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.meta.generation != generationOf(h) || !e.obj || e.meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
|
||||
auto &e = mSlots[idx];
|
||||
if (!e.obj || e.meta.flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectHeader *obj = e.obj;
|
||||
e.obj = nullptr; // tombstone
|
||||
/*auto newHandleCnt =*/ obj->handleCount.fetch_sub(1, std::memory_order_acq_rel) /* - 1*/;
|
||||
ObjectBase *obj = e.obj;
|
||||
const auto generation = e.meta.generation + 1;
|
||||
e = {}; // Clear entry
|
||||
e.meta.generation = generation;
|
||||
uint32_t handleCount = obj->handleCount.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
||||
|
||||
// bump generation & recycle while still holding the lock
|
||||
e.meta.generation = static_cast<uint16_t>((e.meta.generation + 1) & kGenerationMask);
|
||||
freeList_.push_back(idx);
|
||||
if (idx <= kCompatMaxIndex) {
|
||||
mQuarantine.push_back(idx);
|
||||
if (mQuarantine.size() > kQuarantineLen) {
|
||||
mFreeBelow.push_back(mQuarantine.front());
|
||||
mQuarantine.pop_front();
|
||||
}
|
||||
} else {
|
||||
mFreeAbove.push_back(idx);
|
||||
}
|
||||
lk.unlock();
|
||||
|
||||
// if (newHandleCnt == 0) {
|
||||
// namespaceOnHandleCountZero(obj);
|
||||
// }
|
||||
if (handleCount == 0 && mOnHandleZero) {
|
||||
mOnHandleZero(obj);
|
||||
}
|
||||
detail::deref(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::setInformation(HANDLE h, uint32_t mask, uint32_t value) {
|
||||
bool Handles::setInformation(HANDLE h, uint32_t mask, uint32_t value) {
|
||||
if (isPseudo(h)) {
|
||||
return true; // no-op, success
|
||||
}
|
||||
|
||||
std::unique_lock lk(mu_);
|
||||
std::unique_lock lk(m);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
if (idx >= mSlots.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &e = slots_[idx];
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
auto &e = mSlots[idx];
|
||||
if (!e.obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -126,7 +149,7 @@ bool HandleTable::setInformation(HANDLE h, uint32_t mask, uint32_t value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::getInformation(HANDLE h, uint32_t *outFlags) const {
|
||||
bool Handles::getInformation(HANDLE h, uint32_t *outFlags) const {
|
||||
if (!outFlags) {
|
||||
return false;
|
||||
}
|
||||
@ -134,37 +157,24 @@ bool HandleTable::getInformation(HANDLE h, uint32_t *outFlags) const {
|
||||
*outFlags = 0;
|
||||
return true;
|
||||
}
|
||||
std::shared_lock lk(mu_);
|
||||
std::shared_lock lk(m);
|
||||
const auto idx = indexOf(h);
|
||||
if (idx >= slots_.size()) {
|
||||
if (idx >= mSlots.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto &e = slots_[idx];
|
||||
if (e.meta.generation != generationOf(h) || !e.obj) {
|
||||
const auto &e = mSlots[idx];
|
||||
if (!e.obj) {
|
||||
return false;
|
||||
}
|
||||
*outFlags = e.meta.flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleTable::duplicateTo(HANDLE src, HandleTable &dst, HANDLE *out, uint32_t desiredAccess, bool inherit,
|
||||
uint32_t options) {
|
||||
if (!out)
|
||||
return false;
|
||||
|
||||
// Pseudo-handles: resolve to a Borrow of the live object
|
||||
// if (isPseudo(src)) {
|
||||
// Pin pin = resolvePseudoBorrow(src);
|
||||
// if (!pin)
|
||||
// return false;
|
||||
// const uint32_t granted = desiredAccess; // or compute from type; pseudo has full rights to self
|
||||
// *out = dst.create(pin.obj, granted, inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
bool Handles::duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desiredAccess, bool inherit,
|
||||
uint32_t options) {
|
||||
HandleMeta meta{};
|
||||
Pin<ObjectHeader> pin;
|
||||
if (!get(src, pin, &meta)) {
|
||||
Pin<> obj = get(src, &meta);
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -176,10 +186,53 @@ bool HandleTable::duplicateTo(HANDLE src, HandleTable &dst, HANDLE *out, uint32_
|
||||
|
||||
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? meta.grantedAccess : (desiredAccess & meta.grantedAccess);
|
||||
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
*out = dst.create(pin.obj, effAccess, flags);
|
||||
out = dst.alloc(std::move(obj), effAccess, flags);
|
||||
|
||||
if (closeSource) {
|
||||
close(src);
|
||||
release(src);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Namespace::insert(const std::u16string &name, ObjectBase *obj, bool permanent) {
|
||||
if (name.empty() || !obj) {
|
||||
return false;
|
||||
}
|
||||
std::unique_lock lk(m);
|
||||
// Namespace holds a weak ref
|
||||
const auto [_, inserted] = mTable.try_emplace(name, obj, permanent);
|
||||
return inserted;
|
||||
}
|
||||
|
||||
void Namespace::remove(ObjectBase *obj) {
|
||||
std::unique_lock lk(m);
|
||||
for (auto it = mTable.begin(); it != mTable.end(); ++it) {
|
||||
if (it->second.obj == obj && !it->second.permanent) {
|
||||
mTable.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pin<> Namespace::get(const std::u16string &name) {
|
||||
if (name.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::shared_lock lk(m);
|
||||
auto it = mTable.find(name);
|
||||
if (it == mTable.end()) {
|
||||
return {};
|
||||
}
|
||||
assert(it->second.obj);
|
||||
return Pin<>::acquire(it->second.obj);
|
||||
}
|
||||
|
||||
namespace wibo {
|
||||
|
||||
Namespace g_namespace;
|
||||
Handles &handles() {
|
||||
static Handles table([](ObjectBase *obj) { g_namespace.remove(obj); });
|
||||
return table;
|
||||
}
|
||||
|
||||
} // namespace wibo
|
||||
|
192
handles.h
192
handles.h
@ -3,15 +3,20 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <shared_mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
enum class ObjectType : uint16_t {
|
||||
File,
|
||||
Mapped,
|
||||
Directory,
|
||||
Mapping,
|
||||
Process,
|
||||
Token,
|
||||
Mutex,
|
||||
@ -22,58 +27,60 @@ enum class ObjectType : uint16_t {
|
||||
RegistryKey,
|
||||
};
|
||||
|
||||
struct ObjectHeader {
|
||||
struct ObjectBase {
|
||||
const ObjectType type;
|
||||
std::atomic<uint32_t> pointerCount{1};
|
||||
std::atomic<uint32_t> pointerCount{0};
|
||||
std::atomic<uint32_t> handleCount{0};
|
||||
|
||||
explicit ObjectHeader(ObjectType t) : type(t) {}
|
||||
virtual ~ObjectHeader() = default;
|
||||
explicit ObjectBase(ObjectType t) : type(t) {}
|
||||
virtual ~ObjectBase() = default;
|
||||
|
||||
[[nodiscard]] virtual bool isWaitable() const { return false; }
|
||||
virtual void onDestroy() {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void ref(ObjectHeader *o) { o->pointerCount.fetch_add(1, std::memory_order_acq_rel); }
|
||||
inline void deref(ObjectHeader *o) {
|
||||
inline void ref(ObjectBase *o) { o->pointerCount.fetch_add(1, std::memory_order_acq_rel); }
|
||||
inline void deref(ObjectBase *o) {
|
||||
if (o->pointerCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
o->onDestroy();
|
||||
delete o;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct WaitableObject : ObjectHeader {
|
||||
struct WaitableObject : ObjectBase {
|
||||
std::atomic<bool> signaled{false};
|
||||
std::mutex m;
|
||||
std::condition_variable_any cv;
|
||||
|
||||
using ObjectHeader::ObjectHeader;
|
||||
using ObjectBase::ObjectBase;
|
||||
[[nodiscard]] bool isWaitable() const override { return true; }
|
||||
};
|
||||
|
||||
template <class T> struct Pin {
|
||||
enum class Tag { Acquire, Adopt };
|
||||
template <class T = ObjectBase> struct Pin {
|
||||
static_assert(std::is_base_of_v<ObjectBase, T> || std::is_same_v<ObjectBase, T>,
|
||||
"Pin<T>: T must be ObjectBase or derive from it");
|
||||
|
||||
T *obj = nullptr;
|
||||
|
||||
Pin() = default;
|
||||
Pin(T *o, Tag t) : obj(o) {
|
||||
enum class Tag { Acquire, Adopt };
|
||||
template <class U, class = std::enable_if_t<std::is_convertible<U *, T *>::value>>
|
||||
explicit Pin(U *p, Tag t) : obj(static_cast<T *>(p)) {
|
||||
if (obj && t == Tag::Acquire) {
|
||||
detail::ref(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static Pin acquire(ObjectHeader *o) { return {o, Tag::Acquire}; }
|
||||
static Pin adopt(ObjectHeader *o) { return {o, Tag::Adopt}; }
|
||||
|
||||
Pin(const Pin &) = delete;
|
||||
Pin &operator=(const Pin &) = delete;
|
||||
|
||||
Pin(Pin &&other) noexcept : obj(std::exchange(other.obj, nullptr)) {}
|
||||
template <class U, class = std::enable_if_t<std::is_base_of<T, U>::value>> Pin &operator=(Pin<U> &&other) noexcept {
|
||||
reset();
|
||||
obj = std::exchange(other.obj, nullptr);
|
||||
return *this;
|
||||
}
|
||||
template <class U, class = std::enable_if_t<std::is_base_of<T, U>::value>>
|
||||
Pin(Pin<U> &&other) noexcept : obj(std::exchange(other.obj, nullptr)) {} // NOLINT(google-explicit-constructor)
|
||||
Pin &operator=(Pin &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
@ -84,7 +91,10 @@ template <class T> struct Pin {
|
||||
|
||||
~Pin() { reset(); }
|
||||
|
||||
ObjectHeader *release() { return std::exchange(obj, nullptr); }
|
||||
static Pin acquire(T *o) { return Pin{o, Tag::Acquire}; }
|
||||
static Pin adopt(T *o) { return Pin{o, Tag::Adopt}; }
|
||||
|
||||
[[nodiscard]] T *release() { return std::exchange(obj, nullptr); }
|
||||
void reset() {
|
||||
if (obj) {
|
||||
detail::deref(obj);
|
||||
@ -92,12 +102,23 @@ template <class T> struct Pin {
|
||||
}
|
||||
}
|
||||
|
||||
T *operator->() const { return obj; }
|
||||
T &operator*() const { return *obj; }
|
||||
T *operator->() const {
|
||||
assert(obj);
|
||||
return obj;
|
||||
}
|
||||
T &operator*() const {
|
||||
assert(obj);
|
||||
return *obj;
|
||||
}
|
||||
[[nodiscard]] T *get() const { return obj; }
|
||||
[[nodiscard]] Pin<T> clone() const { return Pin<T>::acquire(obj); }
|
||||
explicit operator bool() const { return obj != nullptr; }
|
||||
|
||||
template <typename U> Pin<U> downcast() && {
|
||||
static_assert(std::is_base_of_v<ObjectBase, U>, "U must derive from ObjectBase");
|
||||
if constexpr (std::is_same_v<T, U>) {
|
||||
return std::move(*this);
|
||||
}
|
||||
if (obj && obj->type == U::kType) {
|
||||
auto *u = static_cast<U *>(obj);
|
||||
obj = nullptr;
|
||||
@ -107,6 +128,12 @@ template <class T> struct Pin {
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... Args>
|
||||
Pin<T> make_pin(Args &&...args) noexcept(std::is_nothrow_constructible_v<T, Args...>) {
|
||||
T *p = new T(std::forward<Args>(args)...);
|
||||
return Pin<T>::acquire(p);
|
||||
}
|
||||
|
||||
constexpr DWORD HANDLE_FLAG_INHERIT = 0x1;
|
||||
constexpr DWORD HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x2;
|
||||
|
||||
@ -114,62 +141,113 @@ constexpr DWORD DUPLICATE_CLOSE_SOURCE = 0x1;
|
||||
constexpr DWORD DUPLICATE_SAME_ACCESS = 0x2;
|
||||
|
||||
struct HandleMeta {
|
||||
uint32_t grantedAccess; // effective access mask for this handle
|
||||
uint32_t flags; // inherit/protect/etc
|
||||
ObjectType typeCache; // cached ObjectType for fast getAs
|
||||
uint16_t generation; // must match handle’s generation
|
||||
uint32_t grantedAccess;
|
||||
uint32_t flags;
|
||||
ObjectType typeCache;
|
||||
uint16_t generation;
|
||||
};
|
||||
|
||||
// template <typename T>
|
||||
// struct HandleRef {
|
||||
// Pin<T> obj;
|
||||
// HandleMeta meta;
|
||||
// We have to stay under a HANDLE value of 0xFFFF for legacy applications,
|
||||
// and handles values are aligned to 4.
|
||||
constexpr DWORD MAX_HANDLES = 0x4000;
|
||||
|
||||
// HandleRef() = default;
|
||||
// HandleRef(Pin<T> o, HandleMeta m) : obj(std::move(o)), meta(m) {}
|
||||
|
||||
// explicit operator bool() const { return obj.operator bool(); }
|
||||
// };
|
||||
|
||||
class HandleTable {
|
||||
class Handles {
|
||||
public:
|
||||
HANDLE create(ObjectHeader *obj, uint32_t grantedAccess, uint32_t flags);
|
||||
bool close(HANDLE h);
|
||||
bool get(HANDLE h, Pin<ObjectHeader> &pinOut, HandleMeta *metaOut = nullptr);
|
||||
using OnHandleZeroFn = void (*)(ObjectBase *);
|
||||
explicit Handles(OnHandleZeroFn cb) : mOnHandleZero(cb) {}
|
||||
|
||||
HANDLE alloc(Pin<> obj, uint32_t grantedAccess, uint32_t flags);
|
||||
bool release(HANDLE h);
|
||||
Pin<> get(HANDLE h, HandleMeta *metaOut = nullptr);
|
||||
template <typename T> Pin<T> getAs(HANDLE h, HandleMeta *metaOut = nullptr) {
|
||||
static_assert(std::is_base_of_v<ObjectHeader, T>, "T must derive from ObjectHeader");
|
||||
Pin<ObjectHeader> pin;
|
||||
static_assert(std::is_base_of_v<ObjectBase, T>, "T must derive from ObjectBase");
|
||||
HandleMeta metaOutLocal{};
|
||||
if (!metaOut) {
|
||||
metaOut = &metaOutLocal;
|
||||
}
|
||||
if (!get(h, pin, metaOut)) {
|
||||
auto obj = get(h, metaOut);
|
||||
if (!obj) {
|
||||
return {};
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ObjectHeader>) {
|
||||
return std::move(pin);
|
||||
} else if (metaOut->typeCache != T::kType || pin->type != T::kType) {
|
||||
if constexpr (std::is_same_v<T, ObjectBase>) {
|
||||
return std::move(obj);
|
||||
} else if (metaOut->typeCache != T::kType || obj->type != T::kType) {
|
||||
return {};
|
||||
} else {
|
||||
// Cast directly to T* and transfer ownership to Pin<T>
|
||||
return Pin<T>::adopt(static_cast<T *>(pin.release()));
|
||||
return Pin<T>::adopt(static_cast<T *>(obj.release()));
|
||||
}
|
||||
}
|
||||
bool setInformation(HANDLE h, uint32_t mask, uint32_t value);
|
||||
bool getInformation(HANDLE h, uint32_t *outFlags) const;
|
||||
bool duplicateTo(HANDLE src, HandleTable &dst, HANDLE *out, uint32_t desiredAccess, bool inherit, uint32_t options);
|
||||
bool duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desiredAccess, bool inherit, uint32_t options);
|
||||
|
||||
private:
|
||||
struct HandleEntry {
|
||||
struct ObjectHeader *obj;
|
||||
struct Entry {
|
||||
ObjectBase *obj;
|
||||
HandleMeta meta;
|
||||
};
|
||||
|
||||
std::vector<HandleEntry> slots_;
|
||||
std::vector<uint32_t> freeList_;
|
||||
mutable std::shared_mutex mu_;
|
||||
mutable std::shared_mutex m;
|
||||
std::vector<Entry> mSlots;
|
||||
OnHandleZeroFn mOnHandleZero = nullptr;
|
||||
std::vector<uint32_t> mFreeBelow;
|
||||
std::vector<uint32_t> mFreeAbove;
|
||||
std::deque<uint32_t> mQuarantine;
|
||||
uint32_t nextIndex = 0;
|
||||
};
|
||||
|
||||
class Namespace {
|
||||
public:
|
||||
bool insert(const std::u16string &name, ObjectBase *obj, bool permanent = false);
|
||||
void remove(ObjectBase *obj);
|
||||
Pin<> get(const std::u16string &name);
|
||||
|
||||
template <typename T> Pin<T> getAs(const std::u16string &name) {
|
||||
if (auto pin = get(name)) {
|
||||
return std::move(pin).downcast<T>();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename F, typename Ptr = std::invoke_result_t<F &>,
|
||||
typename T = std::remove_pointer_t<std::decay_t<Ptr>>,
|
||||
std::enable_if_t<std::is_pointer<std::decay_t<Ptr>>::value, int> = 0>
|
||||
std::pair<Pin<T>, bool> getOrCreate(const std::u16string &name, F &&make) {
|
||||
if (name.empty()) {
|
||||
// No name: create unconditionally
|
||||
T *raw = std::invoke(std::forward<F>(make));
|
||||
return {Pin<T>::acquire(raw), true};
|
||||
}
|
||||
if (auto existing = get(name)) {
|
||||
// Return even if downcast fails (don't use getAs<T>)
|
||||
return {std::move(existing).downcast<T>(), false};
|
||||
}
|
||||
T *raw = std::invoke(std::forward<F>(make));
|
||||
Pin<T> newObj = Pin<T>::acquire(raw);
|
||||
if (!newObj) {
|
||||
return {Pin<T>{}, false};
|
||||
}
|
||||
if (!insert(name, newObj.get())) {
|
||||
// Race: someone else inserted it first
|
||||
return {getAs<T>(name), false};
|
||||
}
|
||||
return {std::move(newObj), true};
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
ObjectBase *obj;
|
||||
bool permanent;
|
||||
Entry(ObjectBase *o, bool p) : obj(o), permanent(p) {}
|
||||
};
|
||||
|
||||
mutable std::shared_mutex m;
|
||||
std::unordered_map<std::u16string, Entry> mTable;
|
||||
};
|
||||
|
||||
namespace wibo {
|
||||
extern HandleTable &handles();
|
||||
}
|
||||
|
||||
extern Namespace g_namespace;
|
||||
extern Handles &handles();
|
||||
|
||||
} // namespace wibo
|
||||
|
5
main.cpp
5
main.cpp
@ -333,6 +333,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
blockUpper2GB();
|
||||
files::init();
|
||||
wibo::processes().init();
|
||||
|
||||
// Create TIB
|
||||
memset(&tib, 0, sizeof(tib));
|
||||
@ -347,7 +348,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Determine the guest program name
|
||||
auto guestArgs = processes::splitCommandLine(cmdLine.c_str());
|
||||
auto guestArgs = wibo::splitCommandLine(cmdLine.c_str());
|
||||
std::string programName;
|
||||
if (programIndex != -1) {
|
||||
programName = argv[programIndex];
|
||||
@ -360,7 +361,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Resolve the guest program path
|
||||
std::filesystem::path resolvedGuestPath = processes::resolveExecutable(programName, true).value_or({});
|
||||
std::filesystem::path resolvedGuestPath = wibo::resolveExecutable(programName, true).value_or({});
|
||||
if (resolvedGuestPath.empty()) {
|
||||
fprintf(stderr, "Failed to resolve path to guest program %s\n", programName.c_str());
|
||||
return 1;
|
||||
|
@ -154,7 +154,7 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::Module *module);
|
||||
|
||||
LockedRegistry registry() {
|
||||
static ModuleRegistry reg;
|
||||
std::unique_lock<std::recursive_mutex> guard(reg.mutex);
|
||||
std::unique_lock guard(reg.mutex);
|
||||
if (!reg.initialized) {
|
||||
reg.initialized = true;
|
||||
const wibo::Module *builtins[] = {
|
||||
|
669
processes.cpp
669
processes.cpp
@ -1,5 +1,6 @@
|
||||
#include "processes.h"
|
||||
#include "common.h"
|
||||
#include "dll/kernel32/internal.h"
|
||||
#include "files.h"
|
||||
#include "handles.h"
|
||||
#include <algorithm>
|
||||
@ -7,300 +8,484 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <spawn.h>
|
||||
#include <strings.h>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
extern "C" char **environ;
|
||||
namespace {
|
||||
|
||||
namespace processes {
|
||||
void *allocProcessHandle(pid_t pid) {
|
||||
auto* process = new Process;
|
||||
process->pid = pid;
|
||||
process->exitCode = STILL_ACTIVE;
|
||||
process->forcedExitCode = STILL_ACTIVE;
|
||||
process->terminationRequested = false;
|
||||
inline DWORD decodeExitCode(const siginfo_t &si) {
|
||||
switch (si.si_code) {
|
||||
case CLD_EXITED:
|
||||
return static_cast<DWORD>(si.si_status);
|
||||
case CLD_KILLED:
|
||||
case CLD_DUMPED:
|
||||
return 0xC0000000u | static_cast<DWORD>(si.si_status);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return handles::allocDataHandle(handles::Data{handles::TYPE_PROCESS, (void*)process, 0});
|
||||
} // namespace
|
||||
|
||||
namespace wibo {
|
||||
|
||||
ProcessManager::~ProcessManager() { shutdown(); }
|
||||
|
||||
bool ProcessManager::init() {
|
||||
if (mRunning.load(std::memory_order_acquire)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Process* processFromHandle(void *handle, bool pop) {
|
||||
handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
if (data.type == handles::TYPE_PROCESS) {
|
||||
return (Process*)data.ptr;
|
||||
} else {
|
||||
printf("Invalid file handle %p\n", handle);
|
||||
assert(0);
|
||||
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (mEpollFd < 0) {
|
||||
perror("epoll_create1");
|
||||
return false;
|
||||
}
|
||||
|
||||
mWakeFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (mWakeFd < 0) {
|
||||
perror("eventfd");
|
||||
close(mEpollFd);
|
||||
mEpollFd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
epoll_event ev{};
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = mWakeFd;
|
||||
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeFd, &ev) < 0) {
|
||||
perror("epoll_ctl");
|
||||
close(mWakeFd);
|
||||
mWakeFd = -1;
|
||||
close(mEpollFd);
|
||||
mEpollFd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
mRunning.store(true, std::memory_order_release);
|
||||
mThread = std::thread(&ProcessManager::runLoop, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessManager::shutdown() {
|
||||
if (!mRunning.exchange(false, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
wake();
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
std::lock_guard lk(m);
|
||||
mReg.clear();
|
||||
if (mWakeFd >= 0) {
|
||||
close(mWakeFd);
|
||||
mWakeFd = -1;
|
||||
}
|
||||
if (mEpollFd >= 0) {
|
||||
close(mEpollFd);
|
||||
mEpollFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessManager::addProcess(Pin<ProcessObject> po) {
|
||||
if (!po) {
|
||||
return false;
|
||||
}
|
||||
int pidfd;
|
||||
{
|
||||
std::lock_guard lk(po->m);
|
||||
pidfd = po->pidfd;
|
||||
if (pidfd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
epoll_event ev{};
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = pidfd;
|
||||
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, pidfd, &ev) < 0) {
|
||||
perror("epoll_ctl");
|
||||
close(pidfd);
|
||||
po->pidfd = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasDirectoryComponent(const std::string &command) {
|
||||
return command.find('/') != std::string::npos || command.find('\\') != std::string::npos ||
|
||||
command.find(':') != std::string::npos;
|
||||
{
|
||||
std::lock_guard lk(m);
|
||||
mReg.emplace(pidfd, std::move(po));
|
||||
}
|
||||
wake();
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
void ProcessManager::runLoop() {
|
||||
constexpr int kMaxEvents = 64;
|
||||
std::array<epoll_event, kMaxEvents> events{};
|
||||
while (mRunning.load(std::memory_order_acquire)) {
|
||||
int n = epoll_wait(mEpollFd, events.data(), kMaxEvents, -1);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
std::string part = raw.substr(start, end - start);
|
||||
if (!part.empty()) {
|
||||
if (part[0] != '.') {
|
||||
part.insert(part.begin(), '.');
|
||||
perror("epoll_wait");
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const auto &ev = events[i];
|
||||
if (ev.data.fd == mWakeFd) {
|
||||
// Drain eventfd
|
||||
uint64_t n;
|
||||
while (read(mWakeFd, &n, sizeof(n)) == sizeof(n)) {
|
||||
}
|
||||
exts.push_back(part);
|
||||
continue;
|
||||
}
|
||||
if (end == raw.size()) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
checkPidfd(ev.data.fd);
|
||||
}
|
||||
if (exts.empty()) {
|
||||
exts = {".COM", ".EXE", ".BAT", ".CMD"};
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessManager::wake() const {
|
||||
if (mWakeFd < 0) {
|
||||
return;
|
||||
}
|
||||
uint64_t n = 1;
|
||||
write(mWakeFd, &n, sizeof(n));
|
||||
}
|
||||
|
||||
void ProcessManager::checkPidfd(int pidfd) {
|
||||
siginfo_t si{};
|
||||
si.si_code = CLD_DUMPED;
|
||||
if (pidfd >= 0) {
|
||||
int rc = waitid(P_PIDFD, pidfd, &si, WEXITED | WNOHANG);
|
||||
if (rc < 0) {
|
||||
// TODO: what to do here?
|
||||
perror("waitid");
|
||||
} else if (rc == 0 && si.si_pid == 0) {
|
||||
return;
|
||||
}
|
||||
return exts;
|
||||
epoll_ctl(mEpollFd, EPOLL_CTL_DEL, pidfd, nullptr);
|
||||
close(pidfd);
|
||||
}
|
||||
|
||||
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;
|
||||
Pin<ProcessObject> po;
|
||||
{
|
||||
std::shared_lock lk(m);
|
||||
auto it = mReg.find(pidfd);
|
||||
if (it == mReg.end()) {
|
||||
return;
|
||||
}
|
||||
return paths;
|
||||
po = it->second.clone();
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> buildSearchDirectories() {
|
||||
std::vector<std::filesystem::path> dirs;
|
||||
if (wibo::guestExecutablePath.has_parent_path()) {
|
||||
dirs.push_back(wibo::guestExecutablePath.parent_path());
|
||||
{
|
||||
std::lock_guard lk(po->m);
|
||||
po->signaled.store(true, std::memory_order_release);
|
||||
po->pidfd = -1;
|
||||
if (!po->forcedExitCode) {
|
||||
po->exitCode = decodeExitCode(si);
|
||||
}
|
||||
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;
|
||||
}
|
||||
po->cv.notify_all();
|
||||
|
||||
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath) {
|
||||
if (command.empty()) {
|
||||
return std::nullopt;
|
||||
{
|
||||
std::lock_guard lk(m);
|
||||
auto it = mReg.find(pidfd);
|
||||
if (it != mReg.end()) {
|
||||
mReg.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> candidates;
|
||||
candidates.push_back(command);
|
||||
if (!hasExtension(command)) {
|
||||
for (const auto &ext : pathextValues()) {
|
||||
candidates.push_back(command + ext);
|
||||
}
|
||||
ProcessManager &processes() {
|
||||
static ProcessManager mgr;
|
||||
return mgr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
if (wibo::guestExecutablePath.has_parent_path()) {
|
||||
dirs.push_back(wibo::guestExecutablePath.parent_path());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static int spawnInternal(const std::vector<std::string> &args, pid_t *pidOut) {
|
||||
std::vector<char *> argv;
|
||||
argv.reserve(args.size() + 2);
|
||||
argv.push_back(const_cast<char *>(wibo::executableName.c_str()));
|
||||
for (auto &arg : args) {
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
std::vector<std::string> candidates;
|
||||
candidates.push_back(command);
|
||||
if (!hasExtension(command)) {
|
||||
for (const auto &ext : pathextValues()) {
|
||||
candidates.push_back(command + ext);
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
DEBUG_LOG("Spawning process: %s, args: [", wibo::executableName.c_str());
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i != 0) {
|
||||
DEBUG_LOG(", ");
|
||||
}
|
||||
DEBUG_LOG("'%s'", args[i].c_str());
|
||||
}
|
||||
DEBUG_LOG("]\n");
|
||||
|
||||
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.c_str(), &actions, nullptr, argv.data(), environ);
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
if (spawnResult != 0) {
|
||||
return spawnResult;
|
||||
}
|
||||
if (pidOut) {
|
||||
*pidOut = pid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, pid_t *pidOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && commandLine.empty())) {
|
||||
return ENOENT;
|
||||
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;
|
||||
};
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(3);
|
||||
if (!commandLine.empty()) {
|
||||
args.emplace_back("--cmdline");
|
||||
args.push_back(commandLine);
|
||||
if (hasDirectoryComponent(command)) {
|
||||
for (const auto &name : candidates) {
|
||||
auto resolved = tryResolveDirect(name);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pidOut);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv, pid_t *pidOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && argv.empty())) {
|
||||
return ENOENT;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argv.size() + 1);
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
args.emplace_back("--");
|
||||
for (const auto &arg : argv) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pidOut);
|
||||
}
|
||||
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine) {
|
||||
std::vector<std::string> result;
|
||||
if (!commandLine) {
|
||||
return result;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static int spawnInternal(const std::vector<std::string> &args, Pin<kernel32::ProcessObject> &pinOut) {
|
||||
std::vector<char *> argv;
|
||||
argv.reserve(args.size() + 2);
|
||||
argv.push_back(const_cast<char *>(wibo::executableName.c_str()));
|
||||
for (auto &arg : args) {
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
DEBUG_LOG("Spawning process: %s, args: [", wibo::executableName.c_str());
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i != 0) {
|
||||
DEBUG_LOG(", ");
|
||||
}
|
||||
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;
|
||||
}
|
||||
DEBUG_LOG("'%s'", args[i].c_str());
|
||||
}
|
||||
DEBUG_LOG("]\n");
|
||||
|
||||
std::vector<char *> childEnv;
|
||||
for (char **e = environ; *e != nullptr; ++e) {
|
||||
if (strncmp(*e, "WIBO_DEBUG_INDENT=", 18) != 0) {
|
||||
childEnv.push_back(*e);
|
||||
}
|
||||
}
|
||||
std::string indent = "WIBO_DEBUG_INDENT=" + std::to_string(wibo::debugIndent + 1);
|
||||
childEnv.push_back(strdup(indent.c_str()));
|
||||
childEnv.push_back(nullptr);
|
||||
|
||||
pid_t pid = -1;
|
||||
int spawnResult = posix_spawn(&pid, wibo::executableName.c_str(), nullptr, nullptr, argv.data(), childEnv.data());
|
||||
if (spawnResult != 0) {
|
||||
return spawnResult;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Spawned process with PID %d\n", pid);
|
||||
|
||||
int pidfd = static_cast<int>(syscall(SYS_pidfd_open, pid, 0));
|
||||
if (pidfd < 0) {
|
||||
perror("pidfd_open");
|
||||
return false;
|
||||
}
|
||||
|
||||
pinOut = make_pin<kernel32::ProcessObject>(pid, pidfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine,
|
||||
Pin<kernel32::ProcessObject> &pinOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && commandLine.empty())) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(3);
|
||||
if (!commandLine.empty()) {
|
||||
args.emplace_back("--cmdline");
|
||||
args.push_back(commandLine);
|
||||
}
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pinOut);
|
||||
}
|
||||
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv,
|
||||
Pin<kernel32::ProcessObject> &pinOut) {
|
||||
if (wibo::executableName.empty() || (applicationName.empty() && argv.empty())) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argv.size() + 1);
|
||||
if (!applicationName.empty()) {
|
||||
args.push_back(applicationName);
|
||||
}
|
||||
args.emplace_back("--");
|
||||
for (const auto &arg : argv) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
return spawnInternal(args, pinOut);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace wibo
|
||||
|
52
processes.h
52
processes.h
@ -1,25 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "dll/kernel32/internal.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <sched.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace processes {
|
||||
struct Process {
|
||||
pid_t pid;
|
||||
uint32_t exitCode;
|
||||
uint32_t forcedExitCode;
|
||||
bool terminationRequested;
|
||||
};
|
||||
using kernel32::ProcessObject;
|
||||
|
||||
void *allocProcessHandle(pid_t pid);
|
||||
Process* processFromHandle(void* hHandle, bool pop);
|
||||
namespace wibo {
|
||||
|
||||
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath);
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, pid_t *pidOut);
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv, pid_t *pidOut);
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine);
|
||||
}
|
||||
class ProcessManager {
|
||||
public:
|
||||
~ProcessManager();
|
||||
bool init();
|
||||
void shutdown();
|
||||
bool addProcess(Pin<ProcessObject> po);
|
||||
bool running() const { return mRunning.load(std::memory_order_acquire); }
|
||||
|
||||
private:
|
||||
void runLoop();
|
||||
void wake() const;
|
||||
void checkPidfd(int pidfd);
|
||||
|
||||
mutable std::shared_mutex m;
|
||||
std::atomic<bool> mRunning = false;
|
||||
std::thread mThread;
|
||||
int mEpollFd = -1;
|
||||
int mWakeFd = -1;
|
||||
std::unordered_map<int, Pin<ProcessObject>> mReg;
|
||||
};
|
||||
|
||||
ProcessManager &processes();
|
||||
|
||||
std::optional<std::filesystem::path> resolveExecutable(const std::string &command, bool searchPath);
|
||||
int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine,
|
||||
Pin<kernel32::ProcessObject> &pinOut);
|
||||
int spawnWithArgv(const std::string &applicationName, const std::vector<std::string> &argv,
|
||||
Pin<kernel32::ProcessObject> &pinOut);
|
||||
std::vector<std::string> splitCommandLine(const char *commandLine);
|
||||
|
||||
} // namespace wibo
|
||||
|
@ -50,7 +50,7 @@ int main(void) {
|
||||
|
||||
SetLastError(0);
|
||||
TEST_CHECK(!HeapDestroy(processHeap));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
|
||||
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -109,9 +109,7 @@ static void test_getoverlappedresult_pending(void) {
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(!GetOverlappedResult(NULL, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||
// Wine leaves the caller-supplied transfer count untouched for the
|
||||
// pending case, so we avoid asserting on the value here.
|
||||
// TEST_CHECK_EQ(42U, transferred);
|
||||
TEST_CHECK_EQ(0U, transferred); // No update if the operation is still pending
|
||||
}
|
||||
|
||||
static void test_overlapped_write(void) {
|
||||
|
@ -93,6 +93,7 @@ static void test_file_mapping(void) {
|
||||
TEST_CHECK(UnmapViewOfFile(view));
|
||||
TEST_CHECK(CloseHandle(mapping));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(GetFileAttributesA("test_virtualquery.tmp") == INVALID_FILE_ATTRIBUTES);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user