diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dcd971..11b9d45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ add_executable(wibo dll/user32.cpp dll/vcruntime.cpp dll/version.cpp + access.cpp errors.cpp files.cpp handles.cpp diff --git a/access_mask.cpp b/access.cpp similarity index 80% rename from access_mask.cpp rename to access.cpp index 7df3d83..7a338d3 100644 --- a/access_mask.cpp +++ b/access.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 diff --git a/access_mask.h b/access.h similarity index 100% rename from access_mask.h rename to access.h diff --git a/common.h b/common.h index 107b441..3f4d56b 100644 --- a/common.h +++ b/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; diff --git a/dll/advapi32/internal.h b/dll/advapi32/internal.h index 8f390ff..1ea1a11 100644 --- a/dll/advapi32/internal.h +++ b/dll/advapi32/internal.h @@ -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; diff --git a/dll/advapi32/processthreadsapi.cpp b/dll/advapi32/processthreadsapi.cpp index 03fc67e..023a3b6 100644 --- a/dll/advapi32/processthreadsapi.cpp +++ b/dll/advapi32/processthreadsapi.cpp @@ -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(ProcessHandle); + if (!obj) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + auto token = make_pin(std::move(obj), DesiredAccess); + *TokenHandle = wibo::handles().alloc(std::move(token), 0, 0); wibo::lastError = ERROR_SUCCESS; return TRUE; } diff --git a/dll/advapi32/securitybaseapi.cpp b/dll/advapi32/securitybaseapi.cpp index 958ed73..12b17a0 100644 --- a/dll/advapi32/securitybaseapi.cpp +++ b/dll/advapi32/securitybaseapi.cpp @@ -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(hExistingToken); + if (!existing) { wibo::lastError = ERROR_INVALID_HANDLE; return FALSE; } - auto *existingToken = static_cast(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(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(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(TokenHandle); + if (!token) { wibo::lastError = ERROR_INVALID_HANDLE; return FALSE; } diff --git a/dll/advapi32/winreg.cpp b/dll/advapi32/winreg.cpp index 1c3e757..617202b 100644 --- a/dll/advapi32/winreg.cpp +++ b/dll/advapi32/winreg.cpp @@ -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 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 predefinedHandleForValue(uintptr_t value) { + static std::array, kPredefinedKeyCount> g_predefinedHandles = [] { + std::array, kPredefinedKeyCount> arr; + for (size_t i = 0; i < kPredefinedKeyCount; ++i) { + arr[i] = make_pin(); + 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 handleDataFromHKeyLocked(HKEY hKey) { uintptr_t raw = reinterpret_cast(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(hKey); + if (!obj || obj->closed) { + return {}; } - return static_cast(data.ptr); + return obj; } bool isPredefinedKeyHandle(HKEY hKey) { uintptr_t raw = reinterpret_cast(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 lock(g_registryMutex); - ensureRegistryInitializedLocked(); - RegistryKeyHandleData *baseHandle = handleDataFromHKeyLocked(hKey); + Pin 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(std::move(targetPath)); + auto handle = wibo::handles().alloc(std::move(obj), 0, 0); *phkResult = reinterpret_cast(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 lock(g_registryMutex); - ensureRegistryInitializedLocked(); - RegistryKeyHandleData *baseHandle = handleDataFromHKeyLocked(hKey); + Pin 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(std::move(targetPath)); + auto handle = wibo::handles().alloc(std::move(obj), 0, 0); *phkResult = reinterpret_cast(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(hKey); + if (!obj || obj->closed) { wibo::lastError = ERROR_INVALID_HANDLE; return ERROR_INVALID_HANDLE; } - auto *handleData = static_cast(data.ptr); - delete handleData; wibo::lastError = ERROR_SUCCESS; return ERROR_SUCCESS; } diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index 8a971fd..bd8e4c0 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -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(UNIX_TIME_ZERO & 0xFFFFFFFFULL), static_cast(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(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(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(hFile, &meta); + auto file = wibo::handles().getAs(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(hFile); + auto file = wibo::handles().getAs(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(hFile, &meta); + auto file = wibo::handles().getAs(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(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; + if (isDirectory) { + fsObject = make_pin(fd); + } else { + auto fileObj = make_pin(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(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(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(static_cast(position) >> 32); } + wibo::lastError = ERROR_SUCCESS; return static_cast(static_cast(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(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(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(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(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(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(size); if (lpFileSizeHigh) { *lpFileSizeHigh = static_cast(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(hFile, &meta); + auto file = wibo::handles().getAs(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(hFile, &meta); + auto file = wibo::handles().getAs(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(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(static_cast(st.st_size) >> 32); - lpFileInformation->nFileSizeLow = static_cast(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(static_cast(st.st_size) >> 32); + lpFileInformation->nFileSizeLow = static_cast(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(hFile); + auto file = wibo::handles().getAs(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; diff --git a/dll/kernel32/fileapi.h b/dll/kernel32/fileapi.h index d7aa78d..f0b5ee7 100644 --- a/dll/kernel32/fileapi.h +++ b/dll/kernel32/fileapi.h @@ -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; diff --git a/dll/kernel32/handleapi.cpp b/dll/kernel32/handleapi.cpp index 1932699..f00d424 100644 --- a/dll/kernel32/handleapi.cpp +++ b/dll/kernel32/handleapi.cpp @@ -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 #include @@ -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(handle); - if (raw == static_cast(-1)) { + if (reinterpret_cast(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(data.ptr); + auto proc = wibo::handles().getAs(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(hSourceHandle); - if (sourceHandleRaw == static_cast(-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(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(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(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(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(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(data.ptr); - } else if (data.type == handles::TYPE_TOKEN) { - delete reinterpret_cast(data.ptr); - } else if (data.type == handles::TYPE_MUTEX) { - releaseMutexObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_EVENT) { - releaseEventObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_THREAD) { - releaseThreadObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_SEMAPHORE) { - releaseSemaphoreObject(reinterpret_cast(data.ptr)); - } else { - success = false; - } - if (!success) { + if (!wibo::handles().release(hObject)) { wibo::lastError = ERROR_INVALID_HANDLE; return FALSE; } diff --git a/dll/kernel32/heapapi.cpp b/dll/kernel32/heapapi.cpp index de69309..24e02b3 100644 --- a/dll/kernel32/heapapi.cpp +++ b/dll/kernel32/heapapi.cpp @@ -7,71 +7,36 @@ #include #include #include -#include #include +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(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(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(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(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(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(HeapInformationClass), HeapInformation, HeapInformationLength); - ensureProcessHeapInitialized(); + auto record = wibo::handles().getAs(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(HeapInformation); + record->compatibility = *static_cast(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(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(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(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(hHeap); if (!record) { + wibo::lastError = ERROR_INVALID_HANDLE; + return static_cast(-1); + } + std::lock_guard lk(record->m); + if (!record->heap) { + wibo::lastError = ERROR_INVALID_PARAMETER; return static_cast(-1); } if (!lpMem) { wibo::lastError = ERROR_INVALID_PARAMETER; return static_cast(-1); } - if (!mi_heap_check_owned(record->heap, const_cast(lpMem))) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return static_cast(-1); - } + // if (!mi_heap_check_owned(record->heap, const_cast(lpMem))) { + // wibo::lastError = ERROR_INVALID_PARAMETER; + // return static_cast(-1); + // } size_t size = mi_usable_size(lpMem); wibo::lastError = ERROR_SUCCESS; return static_cast(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(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; } diff --git a/dll/kernel32/internal.h b/dll/kernel32/internal.h index bf3d4e4..a2cf90e 100644 --- a/dll/kernel32/internal.h +++ b/dll/kernel32/internal.h @@ -1,68 +1,172 @@ #pragma once #include "common.h" -#include "minwinbase.h" +#include "handles.h" +#include "mimalloc.h" #include -#include 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(-1); inline constexpr uintptr_t kPseudoCurrentThreadHandleValue = static_cast(-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 diff --git a/dll/kernel32/ioapiset.cpp b/dll/kernel32/ioapiset.cpp index fab6a24..db634af 100644 --- a/dll/kernel32/ioapiset.cpp +++ b/dll/kernel32/ioapiset.cpp @@ -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(lpOverlapped->Internal); if (status == STATUS_PENDING) { wibo::lastError = ERROR_IO_INCOMPLETE; - if (lpNumberOfBytesTransferred) { - *lpNumberOfBytesTransferred = static_cast(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; } diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp index 7006eba..bfd136c 100644 --- a/dll/kernel32/memoryapi.cpp +++ b/dll/kernel32/memoryapi.cpp @@ -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 #include #include #include @@ -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 owner; DWORD protect = PAGE_NOACCESS; DWORD allocationProtect = PAGE_NOACCESS; DWORD type = MEM_PRIVATE; @@ -46,26 +57,6 @@ struct ViewInfo { std::map 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 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 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(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(blockStart); info.AllocationBase = reinterpret_cast(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(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(); + 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(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(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(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 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::max()) { alignedViewLength = viewLength; } + DWORD protect = mapping->protect; ViewInfo view{}; view.viewBase = reinterpret_cast(viewPtr); view.viewLength = static_cast(alignedViewLength); view.allocationBase = reinterpret_cast(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 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(hFileMappingObject); + if (!mapping) { wibo::lastError = ERROR_INVALID_HANDLE; return nullptr; } - auto *mapping = reinterpret_cast(data.ptr); uint64_t offset = (static_cast(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(hFileMappingObject); + if (!mapping) { wibo::lastError = ERROR_INVALID_HANDLE; return nullptr; } - auto *mapping = reinterpret_cast(data.ptr); uint64_t offset = (static_cast(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 lock(g_viewInfoMutex); + std::unique_lock lk(g_viewInfoMutex); auto it = g_viewInfo.find(reinterpret_cast(lpBaseAddress)); if (it == g_viewInfo.end()) { - lock.unlock(); wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } - ViewInfo info = it->second; + void *base = reinterpret_cast(it->second.allocationBase); + size_t length = it->second.allocationLength; g_viewInfo.erase(it); - lock.unlock(); - if (info.allocationLength != 0) { - munmap(reinterpret_cast(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(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(dwSize), pageSize); size_t length = static_cast(end - start); - std::unique_lock 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 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 lock(g_virtualAllocMutex); + std::unique_lock lk(g_virtualAllocMutex); if (release) { uintptr_t base = reinterpret_cast(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 lock(g_virtualAllocMutex); + std::unique_lock lk(g_virtualAllocMutex); VirtualAllocation *region = lookupRegion(start); if (!region || !rangeWithinRegion(*region, start, static_cast(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; diff --git a/dll/kernel32/namedpipeapi.cpp b/dll/kernel32/namedpipeapi.cpp index 68a25e1..818a5af 100644 --- a/dll/kernel32/namedpipeapi.cpp +++ b/dll/kernel32/namedpipeapi.cpp @@ -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(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(pipeFds[0]); + readObj->shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; + auto writeObj = make_pin(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; } diff --git a/dll/kernel32/processthreadsapi.cpp b/dll/kernel32/processthreadsapi.cpp index d9ffbd0..624c7e3 100644 --- a/dll/kernel32/processthreadsapi.cpp +++ b/dll/kernel32/processthreadsapi.cpp @@ -8,16 +8,20 @@ #include "strutil.h" #include "timeutil.h" +#include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -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 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(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(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(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(hThread); - if (raw == kPseudoCurrentThreadHandleValue) { - return ensureCurrentThreadObject(); - } - if (raw == static_cast(-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(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(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(static_cast(-1))); return reinterpret_cast(static_cast(-1)); @@ -287,8 +200,6 @@ DWORD WIN_FUNC GetCurrentThreadId() { } HANDLE WIN_FUNC GetCurrentThread() { - ThreadObject *obj = ensureCurrentThreadObject(); - (void)obj; HANDLE pseudoHandle = reinterpret_cast(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(hProcess); - bool isPseudoHandle = rawHandle == static_cast(-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(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(hProcess); - bool isPseudoHandle = rawHandle == static_cast(-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(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(hThread); - bool isPseudoHandle = rawThreadHandle == kPseudoCurrentThreadHandleValue || rawThreadHandle == 0 || - rawThreadHandle == static_cast(-1); - if (!isPseudoHandle) { - auto data = handles::dataFromHandle(hThread, false); - if (data.type != handles::TYPE_THREAD) { + if (!isPseudoCurrentThreadHandle(hThread)) { + auto obj = wibo::handles().getAs(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(uExitCode)); + exit(static_cast(uExitCode)); } BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode) { DEBUG_LOG("TerminateProcess(%p, %u)\n", hProcess, uExitCode); if (hProcess == reinterpret_cast(static_cast(-1))) { - ExitProcess(uExitCode); + exit(static_cast(uExitCode)); } - auto data = handles::dataFromHandle(hProcess, false); - if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) { + auto process = wibo::handles().getAs(hProcess); + if (!process) { wibo::lastError = ERROR_INVALID_HANDLE; return FALSE; } - auto *process = reinterpret_cast(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(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(hThread); if (!obj) { wibo::lastError = ERROR_INVALID_HANDLE; return static_cast(-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 obj = make_pin(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(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(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(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(hThread) == kPseudoCurrentThreadHandleValue || + bool isPseudoCurrentThread = reinterpret_cast(hThread) == kernel32::kPseudoCurrentThreadHandleValue || hThread == nullptr || hThread == reinterpret_cast(static_cast(-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 arguments = processes::splitCommandLine(commandLine.c_str()); + std::vector 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 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(pid); lpProcessInformation->dwThreadId = 0; diff --git a/dll/kernel32/processthreadsapi.h b/dll/kernel32/processthreadsapi.h index 10bd76e..d247efc 100644 --- a/dll/kernel32/processthreadsapi.h +++ b/dll/kernel32/processthreadsapi.h @@ -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); diff --git a/dll/kernel32/synchapi.cpp b/dll/kernel32/synchapi.cpp index e984ff8..db3d27a 100644 --- a/dll/kernel32/synchapi.cpp +++ b/dll/kernel32/synchapi.cpp @@ -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 +#include #include #include #include #include #include #include -#include #include 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(dwMilliseconds) * 1000); } -namespace { - -std::mutex mutexRegistryLock; -std::unordered_map namedMutexes; - -std::mutex eventRegistryLock; -std::unordered_map namedEvents; - -std::mutex semaphoreRegistryLock; -std::unordered_map 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(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(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(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 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 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 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(lpName)); - } else { - nameLog = ""; + DEBUG_LOG("CreateMutexW(%p, %d, %ls)\n", lpMutexAttributes, static_cast(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 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(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(lpName)); - } else { - nameLog = ""; + DEBUG_LOG("CreateEventW(%p, %d, %d, %ls)\n", lpEventAttributes, static_cast(bManualReset), + static_cast(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 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(lInitialCount), - static_cast(lMaximumCount), lpName ? reinterpret_cast(lpName) : L""); - (void)lpSemaphoreAttributes; - - std::u16string name = makeMutexName(lpName); - SemaphoreObject *obj = nullptr; - bool alreadyExists = false; - { - std::lock_guard 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(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(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(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(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(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(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(); + 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(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(); + 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(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(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(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(); 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(); + 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(); + 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 diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 7c56dcf..735be30 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -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, diff --git a/dll/kernel32/wincon.cpp b/dll/kernel32/wincon.cpp index de337cc..841d23c 100644 --- a/dll/kernel32/wincon.cpp +++ b/dll/kernel32/wincon.cpp @@ -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(hConsoleOutput); + if (file->fd == STDOUT_FILENO || file->fd == STDERR_FILENO) { auto str = wideStringToString(static_cast(lpBuffer), nNumberOfCharsToWrite); - fprintf(fp, "%s", str.c_str()); + dprintf(file->fd, "%s", str.c_str()); if (lpNumberOfCharsWritten) { *lpNumberOfCharsWritten = nNumberOfCharsToWrite; } diff --git a/dll/kernel32/wow64apiset.cpp b/dll/kernel32/wow64apiset.cpp index 8d1ed09..cf9d373 100644 --- a/dll/kernel32/wow64apiset.cpp +++ b/dll/kernel32/wow64apiset.cpp @@ -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(hProcess); - bool isPseudoHandle = rawHandle == static_cast(-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(hProcess); + if (!obj) { wibo::lastError = ERROR_INVALID_HANDLE; return FALSE; } diff --git a/dll/msvcrt.cpp b/dll/msvcrt.cpp index 87ed292..e95ccc0 100644 --- a/dll/msvcrt.cpp +++ b/dll/msvcrt.cpp @@ -1,4 +1,5 @@ #include "common.h" +#include "dll/kernel32/internal.h" #include #include #include @@ -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 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(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(po->exitCode); } if (mode == P_DETACH) { @@ -2727,7 +2719,7 @@ namespace msvcrt { } // _P_NOWAIT and unknown flags: return process id - return static_cast(pid); + return static_cast(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 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(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(po->exitCode); } if (mode == P_DETACH) { return 0; } - return static_cast(pid); + return static_cast(po->pid); } int WIN_ENTRY _wunlink(const uint16_t *filename){ diff --git a/dll/ntdll.cpp b/dll/ntdll.cpp index f72de92..27679ab 100644 --- a/dll/ntdll.cpp +++ b/dll/ntdll.cpp @@ -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(processHandle); if (rawHandle == static_cast(-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(processHandle); + if (!po) { return false; } - auto *process = reinterpret_cast(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(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 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(ProcessInformation); - auto *buffer = reinterpret_cast(reinterpret_cast(ProcessInformation) + sizeof(UNICODE_STRING)); + auto *buffer = + reinterpret_cast(reinterpret_cast(ProcessInformation) + sizeof(UNICODE_STRING)); std::memcpy(buffer, widePath.data(), stringBytes); size_t characterCount = widePath.empty() ? 0 : widePath.size() - 1; unicode->Length = static_cast(characterCount * sizeof(uint16_t)); diff --git a/errors.h b/errors.h index 93139ba..16bd9f6 100644 --- a/errors.h +++ b/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) diff --git a/files.cpp b/files.cpp index 1843019..25b292b 100644 --- a/files.cpp +++ b/files.cpp @@ -6,12 +6,28 @@ #include #include #include +#include #include -#include +#include #include #include #include +#include #include +#include + +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(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(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 &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(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 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(result.bytesTransferred); + file->filePos = pos + static_cast(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 &offset, +IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const std::optional &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(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(rc); + remaining -= static_cast(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(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(rc); remaining -= static_cast(rc); @@ -252,11 +269,11 @@ IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, cons }; if (updateFilePointer || !offset.has_value()) { - std::unique_lock 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(result.bytesTransferred); + file->filePos = pos + static_cast(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(STDIN_FILENO); + stdinObject->closeOnDestroy = false; + stdinHandle = handles.alloc(std::move(stdinObject), FILE_GENERIC_READ, 0); + auto stdoutObject = make_pin(STDOUT_FILENO); + stdoutObject->closeOnDestroy = false; + stdoutHandle = handles.alloc(std::move(stdoutObject), FILE_GENERIC_WRITE, 0); + auto stderrObject = make_pin(STDERR_FILENO); + stderrObject->closeOnDestroy = false; + stderrHandle = handles.alloc(std::move(stderrObject), FILE_GENERIC_WRITE, 0); } std::optional findCaseInsensitiveFile(const std::filesystem::path &directory, diff --git a/files.h b/files.h index 4dd32af..c265e70 100644 --- a/files.h +++ b/files.h @@ -1,55 +1,39 @@ #pragma once #include "common.h" -#include "handles.h" +#include "dll/kernel32/internal.h" #include #include -#include #include #include +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 &offset, + bool updateFilePointer); +IOResult write(FileObject *file, const void *buffer, size_t bytesToWrite, const std::optional &offset, + bool updateFilePointer); +HANDLE getStdHandle(DWORD nStdHandle); +BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle); +std::optional 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 &offset, bool updateFilePointer); - IOResult write(FileObject *handle, const void *buffer, size_t bytesToWrite, const std::optional &offset, bool updateFilePointer); - HANDLE getStdHandle(DWORD nStdHandle); - BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle); - void init(); - std::optional 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; diff --git a/handles.cpp b/handles.cpp index 2479734..cccb5df 100644 --- a/handles.cpp +++ b/handles.cpp @@ -1,121 +1,144 @@ #include "handles.h" +#include +#include 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(h) & kIndexMask; } -inline uint32_t generationOf(HANDLE h) { return (reinterpret_cast(h) >> kGenerationShift) & kGenerationMask; } -inline HANDLE makeHandle(uint32_t index, uint32_t gen) { - return reinterpret_cast((gen << kGenerationShift) | index); +inline uint32_t indexOf(HANDLE h) { + uint32_t v = static_cast(reinterpret_cast(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(static_cast(v)); +} + inline bool isPseudo(HANDLE h) { return reinterpret_cast(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(slots_.size()); - slots_.push_back(HandleEntry{}); + idx = static_cast(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 &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::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((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 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 diff --git a/handles.h b/handles.h index a64b663..261939c 100644 --- a/handles.h +++ b/handles.h @@ -3,15 +3,20 @@ #include "common.h" #include +#include #include #include +#include +#include +#include #include #include #include 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 pointerCount{1}; + std::atomic pointerCount{0}; std::atomic 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 signaled{false}; std::mutex m; std::condition_variable_any cv; - using ObjectHeader::ObjectHeader; + using ObjectBase::ObjectBase; [[nodiscard]] bool isWaitable() const override { return true; } }; -template struct Pin { - enum class Tag { Acquire, Adopt }; +template struct Pin { + static_assert(std::is_base_of_v || std::is_same_v, + "Pin: 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 ::value>> + explicit Pin(U *p, Tag t) : obj(static_cast(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 ::value>> Pin &operator=(Pin &&other) noexcept { + reset(); + obj = std::exchange(other.obj, nullptr); + return *this; + } + template ::value>> + Pin(Pin &&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 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 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 clone() const { return Pin::acquire(obj); } explicit operator bool() const { return obj != nullptr; } template Pin downcast() && { + static_assert(std::is_base_of_v, "U must derive from ObjectBase"); + if constexpr (std::is_same_v) { + return std::move(*this); + } if (obj && obj->type == U::kType) { auto *u = static_cast(obj); obj = nullptr; @@ -107,6 +128,12 @@ template struct Pin { } }; +template +Pin make_pin(Args &&...args) noexcept(std::is_nothrow_constructible_v) { + T *p = new T(std::forward(args)...); + return Pin::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 -// struct HandleRef { -// Pin 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 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 &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 Pin getAs(HANDLE h, HandleMeta *metaOut = nullptr) { - static_assert(std::is_base_of_v, "T must derive from ObjectHeader"); - Pin pin; + static_assert(std::is_base_of_v, "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) { - return std::move(pin); - } else if (metaOut->typeCache != T::kType || pin->type != T::kType) { + if constexpr (std::is_same_v) { + 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 - return Pin::adopt(static_cast(pin.release())); + return Pin::adopt(static_cast(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 slots_; - std::vector freeList_; - mutable std::shared_mutex mu_; + mutable std::shared_mutex m; + std::vector mSlots; + OnHandleZeroFn mOnHandleZero = nullptr; + std::vector mFreeBelow; + std::vector mFreeAbove; + std::deque 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 Pin getAs(const std::u16string &name) { + if (auto pin = get(name)) { + return std::move(pin).downcast(); + } + return {}; + } + + template , + typename T = std::remove_pointer_t>, + std::enable_if_t>::value, int> = 0> + std::pair, bool> getOrCreate(const std::u16string &name, F &&make) { + if (name.empty()) { + // No name: create unconditionally + T *raw = std::invoke(std::forward(make)); + return {Pin::acquire(raw), true}; + } + if (auto existing = get(name)) { + // Return even if downcast fails (don't use getAs) + return {std::move(existing).downcast(), false}; + } + T *raw = std::invoke(std::forward(make)); + Pin newObj = Pin::acquire(raw); + if (!newObj) { + return {Pin{}, false}; + } + if (!insert(name, newObj.get())) { + // Race: someone else inserted it first + return {getAs(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 mTable; }; namespace wibo { -extern HandleTable &handles(); -} + +extern Namespace g_namespace; +extern Handles &handles(); + +} // namespace wibo diff --git a/main.cpp b/main.cpp index 3cc8c10..2426801 100644 --- a/main.cpp +++ b/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; diff --git a/module_registry.cpp b/module_registry.cpp index 38f7df4..5b4b498 100644 --- a/module_registry.cpp +++ b/module_registry.cpp @@ -154,7 +154,7 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::Module *module); LockedRegistry registry() { static ModuleRegistry reg; - std::unique_lock guard(reg.mutex); + std::unique_lock guard(reg.mutex); if (!reg.initialized) { reg.initialized = true; const wibo::Module *builtins[] = { diff --git a/processes.cpp b/processes.cpp index d7e05c2..1dd7c45 100644 --- a/processes.cpp +++ b/processes.cpp @@ -1,5 +1,6 @@ #include "processes.h" #include "common.h" +#include "dll/kernel32/internal.h" #include "files.h" #include "handles.h" #include @@ -7,300 +8,484 @@ #include #include #include +#include #include +#include #include -#include #include +#include +#include +#include +#include #include #include #include -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(si.si_status); + case CLD_KILLED: + case CLD_DUMPED: + return 0xC0000000u | static_cast(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 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 pathextValues() { - const char *envValue = std::getenv("PATHEXT"); - std::string raw = envValue ? envValue : ".COM;.EXE;.BAT;.CMD"; - std::vector 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 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 parseHostPath(const std::string &value) { - std::vector 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 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 buildSearchDirectories() { - std::vector 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 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 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 pathextValues() { + const char *envValue = std::getenv("PATHEXT"); + std::string raw = envValue ? envValue : ".COM;.EXE;.BAT;.CMD"; + std::vector 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 { - 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 parseHostPath(const std::string &value) { + std::vector 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 buildSearchDirectories() { + std::vector 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 resolveExecutable(const std::string &command, bool searchPath) { + if (command.empty()) { return std::nullopt; } - static int spawnInternal(const std::vector &args, pid_t *pidOut) { - std::vector argv; - argv.reserve(args.size() + 2); - argv.push_back(const_cast(wibo::executableName.c_str())); - for (auto &arg : args) { - argv.push_back(const_cast(arg.c_str())); + std::vector 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 { + 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 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 &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 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 splitCommandLine(const char *commandLine) { - std::vector result; - if (!commandLine) { - return result; + return std::nullopt; +} + +static int spawnInternal(const std::vector &args, Pin &pinOut) { + std::vector argv; + argv.reserve(args.size() + 2); + argv.push_back(const_cast(wibo::executableName.c_str())); + for (auto &arg : args) { + argv.push_back(const_cast(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 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(syscall(SYS_pidfd_open, pid, 0)); + if (pidfd < 0) { + perror("pidfd_open"); + return false; + } + + pinOut = make_pin(pid, pidfd); + return 0; +} + +int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, + Pin &pinOut) { + if (wibo::executableName.empty() || (applicationName.empty() && commandLine.empty())) { + return ENOENT; + } + + std::vector 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 &argv, + Pin &pinOut) { + if (wibo::executableName.empty() || (applicationName.empty() && argv.empty())) { + return ENOENT; + } + + std::vector 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 splitCommandLine(const char *commandLine) { + std::vector 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 diff --git a/processes.h b/processes.h index 0ff5709..be195f6 100644 --- a/processes.h +++ b/processes.h @@ -1,25 +1,45 @@ #pragma once -#include +#include "dll/kernel32/internal.h" + #include #include -#include #include +#include #include -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 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 &argv, pid_t *pidOut); - std::vector splitCommandLine(const char *commandLine); -} +class ProcessManager { + public: + ~ProcessManager(); + bool init(); + void shutdown(); + bool addProcess(Pin 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 mRunning = false; + std::thread mThread; + int mEpollFd = -1; + int mWakeFd = -1; + std::unordered_map> mReg; +}; + +ProcessManager &processes(); + +std::optional resolveExecutable(const std::string &command, bool searchPath); +int spawnWithCommandLine(const std::string &applicationName, const std::string &commandLine, + Pin &pinOut); +int spawnWithArgv(const std::string &applicationName, const std::vector &argv, + Pin &pinOut); +std::vector splitCommandLine(const char *commandLine); + +} // namespace wibo diff --git a/test/test_heap.c b/test/test_heap.c index b76ee9e..08907db 100644 --- a/test/test_heap.c +++ b/test/test_heap.c @@ -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; } diff --git a/test/test_overlapped_io.c b/test/test_overlapped_io.c index 5bf5f9d..296c12d 100644 --- a/test/test_overlapped_io.c +++ b/test/test_overlapped_io.c @@ -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) { diff --git a/test/test_virtualquery.c b/test/test_virtualquery.c index 7488afd..43966df 100644 --- a/test/test_virtualquery.c +++ b/test/test_virtualquery.c @@ -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) {