#include "winbase.h" #include "errors.h" #include "files.h" #include "handles.h" #include "internal.h" #include "strutil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr UINT GMEM_MOVEABLE = 0x0002; constexpr UINT GMEM_ZEROINIT = 0x0040; constexpr UINT GMEM_MODIFY = 0x0080; constexpr UINT LMEM_MOVEABLE = 0x0002; constexpr UINT LMEM_ZEROINIT = 0x0040; constexpr ATOM kMinIntegerAtom = 0x0001; constexpr ATOM kMaxIntegerAtom = 0xBFFF; constexpr ATOM kMinStringAtom = 0xC000; constexpr ATOM kMaxStringAtom = 0xFFFF; struct AtomData { uint16_t refCount = 0; std::string original; }; struct AtomTable { std::mutex mutex; std::unordered_map stringToAtom; std::unordered_map atomToData; ATOM nextStringAtom = kMinStringAtom; }; AtomTable &localAtomTable() { static AtomTable table; return table; } ATOM allocateStringAtomLocked(AtomTable &table) { constexpr unsigned int kRange = static_cast(kMaxStringAtom - kMinStringAtom + 1); unsigned int startOffset = 0; if (table.nextStringAtom >= kMinStringAtom && table.nextStringAtom <= kMaxStringAtom) { startOffset = static_cast(table.nextStringAtom - kMinStringAtom); } for (unsigned int i = 0; i < kRange; ++i) { unsigned int offset = (startOffset + i) % kRange; ATOM candidate = static_cast(kMinStringAtom + offset); if (table.atomToData.find(candidate) == table.atomToData.end()) { table.nextStringAtom = static_cast(candidate + 1); if (table.nextStringAtom > kMaxStringAtom) { table.nextStringAtom = kMinStringAtom; } return candidate; } } return 0; } bool tryHandleIntegerAtomPointer(const void *ptr, ATOM &atomOut) { uintptr_t value = reinterpret_cast(ptr); if ((value >> 16) != 0) { return false; } ATOM maybeAtom = static_cast(value & 0xFFFFu); if (maybeAtom < kMinIntegerAtom || maybeAtom > kMaxIntegerAtom) { wibo::lastError = ERROR_INVALID_PARAMETER; atomOut = 0; return true; } wibo::lastError = ERROR_SUCCESS; atomOut = maybeAtom; return true; } ATOM findAtomByNormalizedKey(const std::string &normalizedKey) { auto &table = localAtomTable(); std::lock_guard lk(table.mutex); auto it = table.stringToAtom.find(normalizedKey); if (it == table.stringToAtom.end()) { wibo::lastError = ERROR_FILE_NOT_FOUND; return 0; } wibo::lastError = ERROR_SUCCESS; return it->second; } ATOM tryParseIntegerAtomString(const std::string &value, bool &handled) { handled = false; if (value.empty() || value[0] != '#') { return 0; } char *end = nullptr; unsigned long parsed = std::strtoul(value.c_str() + 1, &end, 10); if (end == value.c_str() + 1 || *end != '\0') { return 0; } handled = true; if (parsed < kMinIntegerAtom || parsed > kMaxIntegerAtom) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } wibo::lastError = ERROR_SUCCESS; return static_cast(parsed); } ATOM findAtomByString(const std::string &value) { bool handledInteger = false; ATOM atom = tryParseIntegerAtomString(value, handledInteger); if (handledInteger) { return atom; } std::string normalized = stringToLower(value); return findAtomByNormalizedKey(normalized); } ATOM addAtomByString(const std::string &value) { bool handledInteger = false; ATOM atom = tryParseIntegerAtomString(value, handledInteger); if (handledInteger) { return atom; } if (value.empty() || value.size() > 255) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string normalized = stringToLower(value); auto &table = localAtomTable(); std::lock_guard lk(table.mutex); auto existing = table.stringToAtom.find(normalized); if (existing != table.stringToAtom.end()) { auto dataIt = table.atomToData.find(existing->second); if (dataIt != table.atomToData.end() && dataIt->second.refCount < std::numeric_limits::max()) { dataIt->second.refCount++; } wibo::lastError = ERROR_SUCCESS; return existing->second; } ATOM newAtom = allocateStringAtomLocked(table); if (newAtom == 0) { wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; return 0; } AtomData data; data.refCount = 1; data.original = value; table.stringToAtom.emplace(std::move(normalized), newAtom); table.atomToData.emplace(newAtom, std::move(data)); wibo::lastError = ERROR_SUCCESS; return newAtom; } void *doAlloc(UINT dwBytes, bool zero) { if (dwBytes == 0) { dwBytes = 1; } void *ret = mi_malloc_aligned(dwBytes, 8); if (ret && zero) { std::memset(ret, 0, mi_usable_size(ret)); } return ret; } void *doRealloc(void *mem, UINT dwBytes, bool zero) { if (dwBytes == 0) { dwBytes = 1; } size_t oldSize = mi_usable_size(mem); void *ret = mi_realloc_aligned(mem, dwBytes, 8); size_t newSize = mi_usable_size(ret); if (ret && zero && newSize > oldSize) { std::memset(static_cast(ret) + oldSize, 0, newSize - oldSize); } return ret; } bool tryGetCurrentDirectoryPath(std::string &outPath) { std::error_code ec; std::filesystem::path cwd = std::filesystem::current_path(ec); if (ec) { errno = ec.value(); kernel32::setLastErrorFromErrno(); return false; } outPath = files::pathToWindows(cwd); return true; } bool computeLongWindowsPath(const std::string &inputPath, std::string &longPath) { bool hasTrailingSlash = false; if (!inputPath.empty()) { char last = inputPath.back(); hasTrailingSlash = (last == '\\' || last == '/'); } auto hostPath = files::pathFromWindows(inputPath.c_str()); if (hostPath.empty()) { wibo::lastError = ERROR_PATH_NOT_FOUND; return false; } std::error_code ec; if (!std::filesystem::exists(hostPath, ec)) { wibo::lastError = ERROR_FILE_NOT_FOUND; return false; } longPath = files::pathToWindows(hostPath); if (hasTrailingSlash && !longPath.empty() && longPath.back() != '\\') { longPath.push_back('\\'); } wibo::lastError = ERROR_SUCCESS; return true; } bool resolveDiskFreeSpaceStat(const char *rootPathName, struct statvfs &outBuf, std::string &resolvedPath) { std::filesystem::path hostPath; if (rootPathName && *rootPathName) { hostPath = files::pathFromWindows(rootPathName); } else { std::error_code ec; hostPath = std::filesystem::current_path(ec); if (ec) { wibo::lastError = ERROR_PATH_NOT_FOUND; return false; } } if (hostPath.empty()) { wibo::lastError = ERROR_PATH_NOT_FOUND; return false; } hostPath = hostPath.lexically_normal(); if (hostPath.empty()) { hostPath = std::filesystem::path("/"); } std::error_code ec; if (!hostPath.is_absolute()) { auto abs = std::filesystem::absolute(hostPath, ec); if (ec) { wibo::lastError = ERROR_PATH_NOT_FOUND; return false; } hostPath = abs; } std::filesystem::path queryPath = hostPath; while (true) { std::string query = queryPath.empty() ? std::string("/") : queryPath.string(); if (query.empty()) { query = "/"; } if (statvfs(query.c_str(), &outBuf) == 0) { resolvedPath = query; wibo::lastError = ERROR_SUCCESS; return true; } int savedErrno = errno; if (savedErrno != ENOENT && savedErrno != ENOTDIR) { errno = savedErrno; kernel32::setLastErrorFromErrno(); return false; } std::filesystem::path parent = queryPath.parent_path(); if (parent == queryPath) { errno = savedErrno; kernel32::setLastErrorFromErrno(); return false; } if (parent.empty()) { parent = std::filesystem::path("/"); } queryPath = parent; } } constexpr DWORD kComputerNameLength = 8; constexpr DWORD kComputerNameRequiredSize = kComputerNameLength + 1; constexpr const char kComputerNameAnsi[] = "COMPNAME"; const uint16_t kComputerNameWide[] = {u'C', u'O', u'M', u'P', u'N', u'A', u'M', u'E', 0}; } // namespace namespace kernel32 { ATOM WIN_FUNC AddAtomA(LPCSTR lpString) { WIN_API_SEGMENT_GUARD(); ATOM atom = 0; if (tryHandleIntegerAtomPointer(lpString, atom)) { DEBUG_LOG("AddAtomA(int:%u)\n", atom); return atom; } DEBUG_LOG("AddAtomA(%s)\n", lpString ? lpString : ""); if (!lpString) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } size_t len = strnlen(lpString, 256); if (len == 0 || len >= 256) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string value(lpString, len); ATOM result = addAtomByString(value); DEBUG_LOG("AddAtomA -> %u (lastError=%u)\n", result, wibo::lastError); return result; } ATOM WIN_FUNC AddAtomW(LPCWSTR lpString) { WIN_API_SEGMENT_GUARD(); ATOM atom = 0; if (tryHandleIntegerAtomPointer(lpString, atom)) { DEBUG_LOG("AddAtomW(int:%u)\n", atom); return atom; } if (!lpString) { DEBUG_LOG("AddAtomW()\n"); wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } size_t len = wstrnlen(reinterpret_cast(lpString), 256); if (len == 0 || len >= 256) { DEBUG_LOG("AddAtomW(invalid length)\n"); wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string value = wideStringToString(reinterpret_cast(lpString), static_cast(len)); DEBUG_LOG("AddAtomW(%s)\n", value.c_str()); ATOM result = addAtomByString(value); DEBUG_LOG("AddAtomW -> %u (lastError=%u)\n", result, wibo::lastError); return result; } ATOM WIN_FUNC FindAtomA(LPCSTR lpString) { WIN_API_SEGMENT_GUARD(); ATOM atom = 0; if (tryHandleIntegerAtomPointer(lpString, atom)) { DEBUG_LOG("FindAtomA(int:%u)\n", atom); return atom; } DEBUG_LOG("FindAtomA(%s)\n", lpString ? lpString : ""); if (!lpString) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } size_t len = strnlen(lpString, 256); if (len == 0 || len >= 256) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string value(lpString, len); ATOM result = findAtomByString(value); DEBUG_LOG("FindAtomA -> %u (lastError=%u)\n", result, wibo::lastError); return result; } ATOM WIN_FUNC FindAtomW(LPCWSTR lpString) { WIN_API_SEGMENT_GUARD(); ATOM atom = 0; if (tryHandleIntegerAtomPointer(lpString, atom)) { DEBUG_LOG("FindAtomW(int:%u)\n", atom); return atom; } if (!lpString) { DEBUG_LOG("FindAtomW()\n"); wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } size_t len = wstrnlen(reinterpret_cast(lpString), 256); if (len == 0 || len >= 256) { DEBUG_LOG("FindAtomW(invalid length)\n"); wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string value = wideStringToString(reinterpret_cast(lpString), static_cast(len)); DEBUG_LOG("FindAtomW(%s)\n", value.c_str()); ATOM result = findAtomByString(value); DEBUG_LOG("FindAtomW -> %u (lastError=%u)\n", result, wibo::lastError); return result; } UINT WIN_FUNC GetAtomNameA(ATOM nAtom, LPSTR lpBuffer, int nSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetAtomNameA(%u, %p, %d)\n", nAtom, lpBuffer, nSize); if (!lpBuffer || nSize <= 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string value; if (nAtom >= kMinIntegerAtom && nAtom <= kMaxIntegerAtom) { value = '#'; value += std::to_string(nAtom); } else { auto &table = localAtomTable(); std::lock_guard lk(table.mutex); auto it = table.atomToData.find(nAtom); if (it == table.atomToData.end()) { wibo::lastError = ERROR_INVALID_HANDLE; return 0; } value = it->second.original; } if (value.size() + 1 > static_cast(nSize)) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::memcpy(lpBuffer, value.c_str(), value.size()); lpBuffer[value.size()] = '\0'; wibo::lastError = ERROR_SUCCESS; UINT written = static_cast(value.size()); DEBUG_LOG("GetAtomNameA -> %u (lastError=%u)\n", written, wibo::lastError); return written; } UINT WIN_FUNC GetAtomNameW(ATOM nAtom, LPWSTR lpBuffer, int nSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetAtomNameW(%u, %p, %d)\n", nAtom, lpBuffer, nSize); if (!lpBuffer || nSize <= 0) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string narrow; if (nAtom >= kMinIntegerAtom && nAtom <= kMaxIntegerAtom) { narrow = '#'; narrow += std::to_string(nAtom); } else { auto &table = localAtomTable(); std::lock_guard lk(table.mutex); auto it = table.atomToData.find(nAtom); if (it == table.atomToData.end()) { wibo::lastError = ERROR_INVALID_HANDLE; return 0; } narrow = it->second.original; } auto wide = stringToWideString(narrow.c_str(), narrow.size()); size_t needed = wide.size(); if (needed > static_cast(nSize)) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::memcpy(lpBuffer, wide.data(), needed * sizeof(uint16_t)); if (needed > 0) { lpBuffer[needed - 1] = 0; } wibo::lastError = ERROR_SUCCESS; UINT written = static_cast(needed ? needed - 1 : 0); DEBUG_LOG("GetAtomNameW -> %u (lastError=%u)\n", written, wibo::lastError); return written; } UINT WIN_FUNC SetHandleCount(UINT uNumber) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("SetHandleCount(%u)\n", uNumber); (void)uNumber; return 0x3FFE; } DWORD WIN_FUNC FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list *Arguments) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("FormatMessageA(%u, %p, %u, %u, %p, %u, %p)\n", dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, Arguments); if (dwFlags & 0x00000100) { // FORMAT_MESSAGE_ALLOCATE_BUFFER } else if (dwFlags & 0x00002000) { // FORMAT_MESSAGE_ARGUMENT_ARRAY } else if (dwFlags & 0x00000800) { // FORMAT_MESSAGE_FROM_HMODULE } else if (dwFlags & 0x00000400) { // FORMAT_MESSAGE_FROM_STRING } else if (dwFlags & 0x00001000) { // FORMAT_MESSAGE_FROM_SYSTEM std::string message = std::system_category().message(static_cast(dwMessageId)); size_t length = message.length(); if (!lpBuffer || nSize == 0) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } std::strncpy(lpBuffer, message.c_str(), static_cast(nSize)); if (static_cast(nSize) <= length) { if (static_cast(nSize) > 0) { lpBuffer[nSize - 1] = '\0'; } wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return 0; } lpBuffer[length] = '\0'; wibo::lastError = ERROR_SUCCESS; return static_cast(length); } else if (dwFlags & 0x00000200) { // FORMAT_MESSAGE_IGNORE_INSERTS } else { // unhandled? } if (lpBuffer && nSize > 0) { lpBuffer[0] = '\0'; } wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; return 0; } PVOID WIN_FUNC EncodePointer(PVOID Ptr) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("EncodePointer(%p)\n", Ptr); return Ptr; } PVOID WIN_FUNC DecodePointer(PVOID Ptr) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("DecodePointer(%p)\n", Ptr); return Ptr; } BOOL WIN_FUNC SetDllDirectoryA(LPCSTR lpPathName) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("SetDllDirectoryA(%s)\n", lpPathName); if (!lpPathName || lpPathName[0] == '\0') { wibo::clearDllDirectoryOverride(); wibo::lastError = ERROR_SUCCESS; return TRUE; } std::filesystem::path hostPath = files::pathFromWindows(lpPathName); if (hostPath.empty() || !std::filesystem::exists(hostPath)) { wibo::lastError = ERROR_PATH_NOT_FOUND; return FALSE; } wibo::setDllDirectoryOverride(std::filesystem::absolute(hostPath)); wibo::lastError = ERROR_SUCCESS; return TRUE; } void tryMarkExecutable(void *mem) { if (!mem) { return; } size_t usable = mi_usable_size(mem); if (usable == 0) { return; } long pageSize = sysconf(_SC_PAGESIZE); if (pageSize <= 0) { return; } uintptr_t start = reinterpret_cast(mem); uintptr_t alignedStart = start & ~static_cast(pageSize - 1); uintptr_t end = (start + usable + pageSize - 1) & ~static_cast(pageSize - 1); size_t length = static_cast(end - alignedStart); if (length == 0) { return; } mprotect(reinterpret_cast(alignedStart), length, PROT_READ | PROT_WRITE | PROT_EXEC); } BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("STUB: IsBadReadPtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); if (!lp) { return TRUE; } return FALSE; } BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("STUB: IsBadWritePtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); if (!lp && ucb != 0) { return TRUE; } return FALSE; } BOOL WIN_FUNC GetComputerNameA(LPSTR lpBuffer, LPDWORD nSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetComputerNameA(%p, %p)\n", lpBuffer, nSize); if (!nSize || !lpBuffer) { if (nSize) { *nSize = 0; } wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } if (*nSize < kComputerNameRequiredSize) { *nSize = kComputerNameRequiredSize; wibo::lastError = ERROR_BUFFER_OVERFLOW; return FALSE; } std::strcpy(lpBuffer, kComputerNameAnsi); *nSize = kComputerNameLength; wibo::lastError = ERROR_SUCCESS; return TRUE; } BOOL WIN_FUNC GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetComputerNameW(%p, %p)\n", lpBuffer, nSize); if (!nSize || !lpBuffer) { if (nSize) { *nSize = 0; } wibo::lastError = ERROR_INVALID_PARAMETER; return FALSE; } if (*nSize < kComputerNameRequiredSize) { *nSize = kComputerNameRequiredSize; wibo::lastError = ERROR_BUFFER_OVERFLOW; return FALSE; } wstrncpy(lpBuffer, kComputerNameWide, static_cast(kComputerNameRequiredSize)); *nSize = kComputerNameLength; wibo::lastError = ERROR_SUCCESS; return TRUE; } HGLOBAL WIN_FUNC GlobalAlloc(UINT uFlags, SIZE_T dwBytes) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("GlobalAlloc(%x, %zu)\n", uFlags, static_cast(dwBytes)); if (uFlags & GMEM_MOVEABLE) { // not implemented rn assert(0); return nullptr; } bool zero = (uFlags & GMEM_ZEROINIT) != 0; return doAlloc(static_cast(dwBytes), zero); } HGLOBAL WIN_FUNC GlobalFree(HGLOBAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("GlobalFree(%p)\n", hMem); std::free(hMem); return nullptr; } HGLOBAL WIN_FUNC GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("GlobalReAlloc(%p, %zu, %x)\n", hMem, static_cast(dwBytes), uFlags); if (uFlags & GMEM_MODIFY) { assert(0); return nullptr; } bool zero = (uFlags & GMEM_ZEROINIT) != 0; return doRealloc(hMem, static_cast(dwBytes), zero); } UINT WIN_FUNC GlobalFlags(HGLOBAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("GlobalFlags(%p)\n", hMem); (void)hMem; return 0; } HLOCAL WIN_FUNC LocalAlloc(UINT uFlags, SIZE_T uBytes) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalAlloc(%x, %zu)\n", uFlags, static_cast(uBytes)); bool zero = (uFlags & LMEM_ZEROINIT) != 0; if ((uFlags & LMEM_MOVEABLE) != 0) { DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); } void *result = doAlloc(static_cast(uBytes), zero); if (!result) { wibo::lastError = ERROR_NOT_SUPPORTED; return nullptr; } // Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalAlloc. tryMarkExecutable(result); DEBUG_LOG(" -> %p\n", result); wibo::lastError = ERROR_SUCCESS; return result; } HLOCAL WIN_FUNC LocalFree(HLOCAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalFree(%p)\n", hMem); // Windows returns NULL on success. std::free(hMem); wibo::lastError = ERROR_SUCCESS; return nullptr; } HLOCAL WIN_FUNC LocalReAlloc(HLOCAL hMem, SIZE_T uBytes, UINT uFlags) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalReAlloc(%p, %zu, %x)\n", hMem, static_cast(uBytes), uFlags); bool zero = (uFlags & LMEM_ZEROINIT) != 0; if ((uFlags & LMEM_MOVEABLE) != 0) { DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); } void *result = doRealloc(hMem, static_cast(uBytes), zero); if (!result && uBytes != 0) { wibo::lastError = ERROR_NOT_SUPPORTED; return nullptr; } // Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalReAlloc. tryMarkExecutable(result); DEBUG_LOG(" -> %p\n", result); wibo::lastError = ERROR_SUCCESS; return result; } HLOCAL WIN_FUNC LocalHandle(LPCVOID pMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalHandle(%p)\n", pMem); return const_cast(pMem); } LPVOID WIN_FUNC LocalLock(HLOCAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalLock(%p)\n", hMem); wibo::lastError = ERROR_SUCCESS; return hMem; } BOOL WIN_FUNC LocalUnlock(HLOCAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalUnlock(%p)\n", hMem); (void)hMem; wibo::lastError = ERROR_SUCCESS; return TRUE; } SIZE_T WIN_FUNC LocalSize(HLOCAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalSize(%p)\n", hMem); return hMem ? mi_usable_size(hMem) : 0; } UINT WIN_FUNC LocalFlags(HLOCAL hMem) { WIN_API_SEGMENT_GUARD(); VERBOSE_LOG("LocalFlags(%p)\n", hMem); (void)hMem; return 0; } static constexpr const char *kSystemDirectoryA = "C:\\Windows\\System32"; UINT WIN_FUNC GetSystemDirectoryA(LPSTR lpBuffer, UINT uSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetSystemDirectoryA(%p, %u)\n", lpBuffer, uSize); if (!lpBuffer) { return 0; } const auto len = std::strlen(kSystemDirectoryA); if (uSize < len + 1) { return static_cast(len + 1); } std::strcpy(lpBuffer, kSystemDirectoryA); return static_cast(len); } UINT WIN_FUNC GetSystemDirectoryW(LPWSTR lpBuffer, UINT uSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetSystemDirectoryW(%p, %u)\n", lpBuffer, uSize); if (!lpBuffer) { return 0; } auto wide = stringToWideString(kSystemDirectoryA); UINT length = static_cast(wide.size() - 1); if (uSize < length + 1) { return length + 1; } std::memcpy(lpBuffer, wide.data(), (length + 1) * sizeof(uint16_t)); return length; } UINT WIN_FUNC GetSystemWow64DirectoryA(LPSTR lpBuffer, UINT uSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetSystemWow64DirectoryA(%p, %u)\n", lpBuffer, uSize); (void)lpBuffer; (void)uSize; wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; return 0; } UINT WIN_FUNC GetSystemWow64DirectoryW(LPWSTR lpBuffer, UINT uSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetSystemWow64DirectoryW(%p, %u)\n", lpBuffer, uSize); (void)lpBuffer; (void)uSize; wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; return 0; } UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetWindowsDirectoryA(%p, %u)\n", lpBuffer, uSize); if (!lpBuffer) { return 0; } const char *windowsDir = "C:\\Windows"; const auto len = std::strlen(windowsDir); if (uSize < len + 1) { return static_cast(len + 1); } std::strcpy(lpBuffer, windowsDir); return static_cast(len); } DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetCurrentDirectoryA(%u, %p)\n", nBufferLength, lpBuffer); std::string path; if (!tryGetCurrentDirectoryPath(path)) { return 0; } const DWORD required = static_cast(path.size() + 1); if (nBufferLength == 0) { return required; } if (!lpBuffer) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (nBufferLength < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return required; } std::memcpy(lpBuffer, path.c_str(), required); wibo::lastError = ERROR_SUCCESS; return required - 1; } DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetCurrentDirectoryW(%u, %p)\n", nBufferLength, lpBuffer); std::string path; if (!tryGetCurrentDirectoryPath(path)) { return 0; } auto widePath = stringToWideString(path.c_str()); const DWORD required = static_cast(widePath.size()); if (nBufferLength == 0) { return required; } if (!lpBuffer) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (nBufferLength < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return required; } std::copy(widePath.begin(), widePath.end(), lpBuffer); wibo::lastError = ERROR_SUCCESS; return required - 1; } int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("SetCurrentDirectoryA(%s)\n", lpPathName ? lpPathName : "(null)"); if (!lpPathName) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } auto hostPath = files::pathFromWindows(lpPathName); std::error_code ec; std::filesystem::current_path(hostPath, ec); if (ec) { errno = ec.value(); kernel32::setLastErrorFromErrno(); return 0; } wibo::lastError = ERROR_SUCCESS; return 1; } int WIN_FUNC SetCurrentDirectoryW(LPCWSTR lpPathName) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("SetCurrentDirectoryW\n"); if (!lpPathName) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string path = wideStringToString(lpPathName); return SetCurrentDirectoryA(path.c_str()); } DWORD WIN_FUNC GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetLongPathNameA(%s, %p, %u)\n", lpszShortPath ? lpszShortPath : "(null)", lpszLongPath, cchBuffer); if (!lpszShortPath) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string input(lpszShortPath); std::string longPath; if (!computeLongWindowsPath(input, longPath)) { return 0; } DWORD required = static_cast(longPath.size() + 1); if (cchBuffer == 0) { return required; } if (!lpszLongPath) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (cchBuffer < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return required; } std::memcpy(lpszLongPath, longPath.c_str(), required); wibo::lastError = ERROR_SUCCESS; return required - 1; } DWORD WIN_FUNC GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetLongPathNameW(%p, %p, %u)\n", lpszShortPath, lpszLongPath, cchBuffer); if (!lpszShortPath) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } std::string input = wideStringToString(lpszShortPath); std::string longPath; if (!computeLongWindowsPath(input, longPath)) { return 0; } auto wideLong = stringToWideString(longPath.c_str()); DWORD required = static_cast(wideLong.size()); if (cchBuffer == 0) { return required; } if (!lpszLongPath) { wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } if (cchBuffer < required) { wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return required; } std::copy(wideLong.begin(), wideLong.end(), lpszLongPath); wibo::lastError = ERROR_SUCCESS; return required - 1; } BOOL WIN_FUNC GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetDiskFreeSpaceA(%s)\n", lpRootPathName ? lpRootPathName : "(null)"); struct statvfs buf{}; std::string resolvedPath; if (!resolveDiskFreeSpaceStat(lpRootPathName, buf, resolvedPath)) { return FALSE; } uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; if (blockSize == 0) { blockSize = 4096; } unsigned int bytesPerSector = 512; if (blockSize % bytesPerSector != 0) { bytesPerSector = static_cast(std::min(blockSize, std::numeric_limits::max())); } unsigned int sectorsPerCluster = static_cast(blockSize / bytesPerSector); if (sectorsPerCluster == 0) { sectorsPerCluster = 1; bytesPerSector = static_cast(std::min(blockSize, std::numeric_limits::max())); } uint64_t totalClusters64 = buf.f_blocks; uint64_t freeClusters64 = buf.f_bavail; if (lpSectorsPerCluster) { *lpSectorsPerCluster = sectorsPerCluster; } if (lpBytesPerSector) { *lpBytesPerSector = bytesPerSector; } if (lpNumberOfFreeClusters) { uint64_t clamped = std::min(freeClusters64, std::numeric_limits::max()); *lpNumberOfFreeClusters = static_cast(clamped); } if (lpTotalNumberOfClusters) { uint64_t clamped = std::min(totalClusters64, std::numeric_limits::max()); *lpTotalNumberOfClusters = static_cast(clamped); } DEBUG_LOG("\t-> host %s, spc %u, bps %u, free clusters %u, total clusters %u\n", resolvedPath.c_str(), lpSectorsPerCluster ? *lpSectorsPerCluster : 0, lpBytesPerSector ? *lpBytesPerSector : 0, lpNumberOfFreeClusters ? *lpNumberOfFreeClusters : 0, lpTotalNumberOfClusters ? *lpTotalNumberOfClusters : 0); wibo::lastError = ERROR_SUCCESS; return TRUE; } BOOL WIN_FUNC GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) { WIN_API_SEGMENT_GUARD(); std::string rootPath = wideStringToString(lpRootPathName); return GetDiskFreeSpaceA(lpRootPathName ? rootPath.c_str() : nullptr, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters); } BOOL WIN_FUNC GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetDiskFreeSpaceExA(%s)\n", lpDirectoryName ? lpDirectoryName : "(null)"); struct statvfs buf{}; std::string resolvedPath; if (!resolveDiskFreeSpaceStat(lpDirectoryName, buf, resolvedPath)) { return FALSE; } uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; if (blockSize == 0) { blockSize = 4096; } uint64_t freeToCaller = static_cast(buf.f_bavail) * blockSize; uint64_t totalBytes = static_cast(buf.f_blocks) * blockSize; uint64_t totalFree = static_cast(buf.f_bfree) * blockSize; if (lpFreeBytesAvailableToCaller) { *lpFreeBytesAvailableToCaller = freeToCaller; } if (lpTotalNumberOfBytes) { *lpTotalNumberOfBytes = totalBytes; } if (lpTotalNumberOfFreeBytes) { *lpTotalNumberOfFreeBytes = totalFree; } DEBUG_LOG("\t-> host %s, free %llu, total %llu, total free %llu\n", resolvedPath.c_str(), static_cast(freeToCaller), static_cast(totalBytes), static_cast(totalFree)); wibo::lastError = ERROR_SUCCESS; return TRUE; } BOOL WIN_FUNC GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { WIN_API_SEGMENT_GUARD(); DEBUG_LOG("GetDiskFreeSpaceExW -> "); std::string directoryName = wideStringToString(lpDirectoryName); return GetDiskFreeSpaceExA(lpDirectoryName ? directoryName.c_str() : nullptr, lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); } } // namespace kernel32