/* Userspace Virtual Filesystem Copyright (C) 2015 Sebastian Herbord. All rights reserved. This file is part of usvfs. usvfs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. usvfs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with usvfs. If not, see . */ #include "winapi.h" #include "exceptionex.h" #include "logging.h" #include "ntdll_declarations.h" #include "stringcast.h" #include "stringutils.h" #include "unicodestring.h" namespace winapi::ansi { std::string getModuleFileName(HMODULE module, HANDLE process) { std::wstring result = wide::getModuleFileName(module, process); return usvfs::shared::string_cast(result); } std::string getCurrentDirectory() { std::string result; DWORD required = GetCurrentDirectoryA(0, nullptr); if (required == 0UL) { throw usvfs::shared::windows_error("failed to determine current working directory"); } result.resize(required); GetCurrentDirectoryA(required, &result[0]); result.resize(required - 1); return result; } std::pair getFullPathName(LPCSTR fileName) { static const int INIT_SIZE = 128; std::string result; result.resize(INIT_SIZE); LPSTR filePart = nullptr; DWORD requiredSize = GetFullPathNameA(fileName, INIT_SIZE, &result[0], &filePart); if (requiredSize >= INIT_SIZE) { result.resize(requiredSize); GetFullPathNameA(fileName, requiredSize, &result[0], &filePart); } if (requiredSize != 0UL) { return std::make_pair(result, std::string(filePart != nullptr ? filePart : "")); } else { return make_pair(result, std::string()); } } } // namespace winapi::ansi namespace winapi::wide { std::wstring getModuleFileName(HMODULE module, HANDLE process) { std::wstring result; result.resize(64); DWORD rc = 0UL; while ((rc = (process == INVALID_HANDLE_VALUE) ? ::GetModuleFileNameW(module, &result[0], static_cast(result.size())) : ::GetModuleFileNameExW(process, module, &result[0], static_cast(result.size()))) == result.size()) { result.resize(result.size() * 2); } if (rc == 0UL) { if (::GetLastError() == ERROR_PARTIAL_COPY) { #if BOOST_ARCH_X86_64 return L"unknown (32-bit process)"; #else return L"unknown (64-bit process)"; #endif } else { throw usvfs::shared::windows_error("failed to retrieve module file name"); } } result.resize(rc); return result; } std::pair getFullPathName(LPCWSTR fileName) { wchar_t buf1[MAX_PATH]; std::vector buf2; wchar_t* result = buf1; LPWSTR filePart = nullptr; DWORD requiredSize = GetFullPathNameW(fileName, MAX_PATH, result, &filePart); if (requiredSize >= MAX_PATH) { buf2.resize(requiredSize); result = &buf2[0]; requiredSize = GetFullPathNameW(fileName, requiredSize, result, &filePart); } return make_pair(std::wstring(result, requiredSize), std::wstring((requiredSize && filePart) ? filePart : L"")); } std::wstring getCurrentDirectory() { // really great api this (::GetCurrentDirectoryW) // - if it succeeds, returns size in characters WITHOUT zero termination // - if it fails due to buffer too small, returns size in characters WITH zero // termination // - if it fails for other reasons, returns 0 std::wstring result; DWORD required = GetCurrentDirectoryW(0, nullptr); if (required == 0UL) { throw usvfs::shared::windows_error("failed to determine current working directory"); } result.resize(required); GetCurrentDirectoryW(required, &result[0]); result.resize(required - 1); return result; } std::wstring getKnownFolderPath(REFKNOWNFOLDERID folderID) { PWSTR writablePath; ::SHGetKnownFolderPath(folderID, 0, nullptr, &writablePath); ON_BLOCK_EXIT([writablePath]() { ::CoTaskMemFree(writablePath); }); return std::wstring(writablePath); } } // namespace winapi::wide namespace winapi::ex { std::pair getSectionRange(HANDLE moduleHandle) { std::pair result; bool found = false; uintptr_t exeModule = reinterpret_cast(moduleHandle); if (exeModule == 0) { throw std::runtime_error("failed to determine address range of executable"); } std::pair totalRange{UINT_MAX, 0}; PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(exeModule); PIMAGE_NT_HEADERS ntHeader = reinterpret_cast(exeModule + dosHeader->e_lfanew); PIMAGE_SECTION_HEADER sectionHeader = reinterpret_cast(ntHeader + 1); for (int i = 0; i < ntHeader->FileHeader.NumberOfSections && !found; ++i) { if (memcmp(sectionHeader->Name, ".text", 5) == 0) { result.first = exeModule + sectionHeader->VirtualAddress; result.second = result.first + sectionHeader->Misc.VirtualSize; found = true; } else { uintptr_t start = exeModule + sectionHeader->VirtualAddress; totalRange.first = std::min(totalRange.first, start); totalRange.second = std::max(totalRange.second, start + sectionHeader->Misc.VirtualSize); } ++sectionHeader; } if (!found) { return totalRange; } return result; } OSVersion getOSVersion() { RTL_OSVERSIONINFOEXW versionInfo; ZeroMemory(&versionInfo, sizeof(RTL_OSVERSIONINFOEXW)); versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo); OSVersion result; result.major = versionInfo.dwMajorVersion; result.minor = versionInfo.dwMinorVersion; result.build = versionInfo.dwBuildNumber; result.platformid = versionInfo.dwPlatformId; result.servicpack = versionInfo.wServicePackMajor << 16 | versionInfo.wServicePackMinor; return result; } } // namespace winapi::ex namespace winapi::ex::ansi { std::string errorString(DWORD errorCode) { std::ostringstream finalMessage; LPSTR buffer = nullptr; DWORD currentErrorCode = GetLastError(); errorCode = errorCode != std::numeric_limits::max() ? errorCode : currentErrorCode; // TODO: the message is not english? if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, errorCode, 0 //, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) , (LPSTR)&buffer, 0, nullptr) == 0) { finalMessage << "(unknown error [" << errorCode << "])"; } else { if (buffer != nullptr) { size_t end = strlen(buffer) - 1; while ((buffer[end] == '\n') || (buffer[end] == '\r')) { buffer[end--] = '\0'; } finalMessage << "(" << buffer << " [" << errorCode << "])"; LocalFree(buffer); // allocated by FormatMessage } } SetLastError(currentErrorCode); // restore error code because FormatMessage might // have modified it return finalMessage.str(); } std::string toString(const FILETIME& time) { SYSTEMTIME temp; FileTimeToSystemTime(&time, &temp); std::ostringstream stream; stream << temp.wYear << "-" << temp.wMonth << "-" << temp.wDay << temp.wHour << ":" << temp.wMinute << ":" << temp.wSecond; return stream.str(); } LPCSTR GetBaseName(LPCSTR string) { LPCSTR result = string + strlen(string) - 1; while (result > string) { if ((*result == '\\') || (*result == '/')) { ++result; break; } else { --result; } } return result; } } // namespace winapi::ex::ansi namespace winapi::ex::wide { bool fileExists(LPCWSTR fileName, bool* isDirectory) { DWORD attrib = GetFileAttributesW(fileName); if (attrib == INVALID_FILE_ATTRIBUTES) { return false; } else { if (isDirectory != nullptr) { *isDirectory = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; } return true; } } std::wstring errorString(DWORD errorCode) { std::wostringstream finalMessage; LPWSTR buffer = nullptr; DWORD currentErrorCode = GetLastError(); errorCode = errorCode != std::numeric_limits::max() ? errorCode : currentErrorCode; // TODO: the message is not english? if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, errorCode, 0 //, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) , (LPWSTR)&buffer, 0, nullptr) == 0) { finalMessage << L"(unknown error [" << errorCode << "])"; } else { if (buffer != nullptr) { size_t end = wcslen(buffer) - 1; while ((buffer[end] == L'\n') || (buffer[end] == L'\r')) { buffer[end--] = L'\0'; } finalMessage << L"(" << buffer << L" [" << errorCode << L"])"; LocalFree(buffer); // allocated by FormatMessage } } SetLastError(currentErrorCode); // restore error code because FormatMessage might // have modified it return finalMessage.str(); } std::wstring toString(const FILETIME& time) { SYSTEMTIME temp; FileTimeToSystemTime(&time, &temp); std::wostringstream stream; stream << temp.wYear << "-" << temp.wMonth << "-" << temp.wDay << temp.wHour << ":" << temp.wMinute << ":" << temp.wSecond; return stream.str(); } LPCWSTR GetBaseName(LPCWSTR string) { LPCWSTR result; if ((string == nullptr) || (string[0] == L'\0')) { result = string; } else { result = string + wcslen(string) - 1; } while (result > string) { if ((*result == L'\\') || (*result == L'/')) { ++result; break; } else { --result; } } return result; } LPWSTR GetBaseName(LPWSTR path) { LPCWSTR result = GetBaseName(static_cast(path)); return const_cast(result); } std::wstring getSectionName(PVOID addressIn, HANDLE process) { if (process == nullptr) { process = GetCurrentProcess(); } HMODULE modules[1024]; intptr_t address = reinterpret_cast(addressIn); DWORD required; if (::EnumProcessModules(process, modules, sizeof(modules), &required)) { for (DWORD i = 0; i < (std::min(1024UL, required) / sizeof(HMODULE)); ++i) { std::pair range = getSectionRange(modules[i]); if ((address > range.first) && (address < range.second)) { try { return winapi::wide::getModuleFileName(modules[i], process); } catch (const std::exception&) { return std::wstring(L"unknown"); } } } } return std::wstring(L"unknown"); } std::vector quickFindFiles(LPCWSTR directoryName, LPCWSTR pattern) { std::vector result; static const unsigned int BUFFER_SIZE = 1024; HANDLE hdl = CreateFileW(directoryName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); ON_BLOCK_EXIT([hdl]() { CloseHandle(hdl); }); uint8_t buffer[BUFFER_SIZE]; NTSTATUS res = STATUS_SUCCESS; // status success while (res == STATUS_SUCCESS) { IO_STATUS_BLOCK status; res = NtQueryDirectoryFile( hdl, nullptr, nullptr, nullptr, &status, buffer, BUFFER_SIZE, FileFullDirectoryInformation, FALSE, static_cast(usvfs::UnicodeString(pattern)), FALSE); if (res == STATUS_SUCCESS) { FILE_FULL_DIR_INFORMATION* info = reinterpret_cast(buffer); void* endPos = buffer + status.Information; while (info < endPos) { FileResult file; file.fileName = std::wstring(info->FileName, info->FileNameLength / sizeof(wchar_t)); file.attributes = info->FileAttributes; result.push_back(file); if (info->NextEntryOffset == 0) { break; } else { info = reinterpret_cast( reinterpret_cast(info) + info->NextEntryOffset); } } } } return result; } bool createPath(boost::filesystem::path path, LPSECURITY_ATTRIBUTES securityAttributes) { // sanity and guaranteed recursion end: if (!path.has_relative_path()) throw usvfs::shared::windows_error( "createPath() refusing to create non-existing top level path: " + path.string()); DWORD attr = GetFileAttributesW(path.c_str()); DWORD err = GetLastError(); if (attr != INVALID_FILE_ATTRIBUTES) { if (attr & FILE_ATTRIBUTE_DIRECTORY) return false; // if directory already exists all is good else throw usvfs::shared::windows_error("createPath() called on a file: " + path.string()); } if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) throw usvfs::shared::windows_error( "createPath() GetFileAttributesW failed on: " + path.string(), err); if (err != ERROR_FILE_NOT_FOUND) // ERROR_FILE_NOT_FOUND means parent directory // already exists createPath(path.parent_path(), securityAttributes); // otherwise create parent directory (recursively) BOOL res = CreateDirectoryW(path.c_str(), securityAttributes); if (!res) { err = GetLastError(); throw usvfs::shared::windows_error( "createPath() CreateDirectoryW failed on: " + path.string(), err); } return true; } std::wstring getWindowsBuildLab(bool ex) { HKEY hKey = nullptr; auto res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0, KEY_READ, &hKey); if (res != ERROR_SUCCESS || !hKey) return L"Opening HKLM Windows NT\\CurrentVersion failed?!"; WCHAR buf[200]; DWORD size = static_cast(sizeof(buf)); res = RegQueryValueExW(hKey, ex ? L"BuildLabEx" : L"BuildLab", NULL, NULL, reinterpret_cast(buf), &size); if (res != ERROR_SUCCESS || size > sizeof(buf)) return ex ? L"BuildLabEx reg value not found?!" : L"BuildLab reg value not found?!"; size /= sizeof(buf[0]); if (size && !buf[size - 1]) --size; return std::wstring(buf, size); } } // namespace winapi::ex::wide