Mercurial > minori
comparison dep/animia/src/fd/win32.cc @ 152:8700806c2cc2
dep/animia: awesome new breaking changes!
I'm so tired
| author | Paper <mrpapersonic@gmail.com> |
|---|---|
| date | Wed, 15 Nov 2023 02:34:59 -0500 |
| parents | 54744a48a7d7 |
| children | cdf79282d647 |
comparison
equal
deleted
inserted
replaced
| 151:54744a48a7d7 | 152:8700806c2cc2 |
|---|---|
| 1 /** | 1 /* |
| 2 * win32.cpp | 2 ** win32.cpp |
| 3 * - provides support for Windows clients | 3 ** - provides support for Windows clients |
| 4 * | 4 ** |
| 5 **/ | 5 */ |
| 6 #include "animia/fd/win32.h" | 6 #include "animia/fd/win32.h" |
| 7 #include "animia/util/win32.h" | |
| 8 #include "animia.h" | |
| 7 | 9 |
| 8 #include <stdexcept> | 10 #include <stdexcept> |
| 9 #include <string> | 11 #include <string> |
| 10 #include <unordered_map> | 12 #include <unordered_map> |
| 11 #include <vector> | 13 #include <vector> |
| 24 /* This file is noticably more complex than Unix and Linux, and that's because | 26 /* This file is noticably more complex than Unix and Linux, and that's because |
| 25 there is no "simple" way to get the paths of a file. In fact, this thing requires | 27 there is no "simple" way to get the paths of a file. In fact, this thing requires |
| 26 you to use *internal functions* that can't even be linked to, hence why we have to | 28 you to use *internal functions* that can't even be linked to, hence why we have to |
| 27 use GetProcAddress and such. What a mess. */ | 29 use GetProcAddress and such. What a mess. */ |
| 28 | 30 |
| 29 #define SystemExtendedHandleInformation ((SYSTEM_INFORMATION_CLASS)0x40) | 31 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for |
| 32 32-bit PIDs, unlike SystemHandleInformation */ | |
| 33 constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40); | |
| 34 | |
| 35 /* more constants not in winternl.h */ | |
| 30 constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; | 36 constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL; |
| 31 constexpr NTSTATUS STATUS_SUCCESS = 0x00000000UL; | 37 |
| 32 | 38 /* this is filled in at runtime because it's not guaranteed to be (and isn't) |
| 39 constant between different versions of Windows */ | |
| 33 static unsigned short file_type_index = 0; | 40 static unsigned short file_type_index = 0; |
| 34 | 41 |
| 35 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { | 42 struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { |
| 36 PVOID Object; | 43 PVOID Object; |
| 37 ULONG_PTR UniqueProcessId; | 44 ULONG_PTR UniqueProcessId; |
| 75 | 82 |
| 76 static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() { | 83 static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() { |
| 77 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res; | 84 std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res; |
| 78 /* we should really put a cap on this */ | 85 /* we should really put a cap on this */ |
| 79 ULONG cb = 1 << 19; | 86 ULONG cb = 1 << 19; |
| 80 NTSTATUS status = STATUS_SUCCESS; | 87 |
| 81 SYSTEM_HANDLE_INFORMATION_EX* info; | 88 for (NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH; status == STATUS_INFO_LENGTH_MISMATCH; ) { |
| 82 | 89 /* why are we doing this? */ |
| 83 do { | |
| 84 status = STATUS_NO_MEMORY; | 90 status = STATUS_NO_MEMORY; |
| 85 | 91 |
| 86 if (!(info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2))) | 92 SYSTEM_HANDLE_INFORMATION_EX* info = (SYSTEM_HANDLE_INFORMATION_EX*)malloc(cb *= 2); |
| 93 if (!info) | |
| 87 continue; | 94 continue; |
| 88 | 95 |
| 89 res.reserve(cb); | 96 res.reserve(cb); |
| 90 | 97 |
| 91 if (0 <= (status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb))) { | 98 status = QuerySystemInformation(SystemExtendedHandleInformation, info, cb, &cb); |
| 92 if (ULONG_PTR handles = info->NumberOfHandles) { | 99 if (0 <= status) { |
| 100 ULONG_PTR handles = info->NumberOfHandles; | |
| 101 if (handles) { | |
| 102 res.reserve(res.size() + handles); | |
| 103 | |
| 93 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles; | 104 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles; |
| 94 do { | 105 do { |
| 95 if (entry) | 106 if (entry) |
| 96 res.push_back(*entry); | 107 res.push_back(*entry); |
| 97 } while (entry++, --handles); | 108 } while (entry++, --handles); |
| 98 } | 109 } |
| 99 } | 110 } |
| 111 | |
| 100 free(info); | 112 free(info); |
| 101 } while (status == STATUS_INFO_LENGTH_MISMATCH); | 113 } |
| 102 | 114 |
| 103 return res; | 115 return res; |
| 104 } | 116 } |
| 105 | 117 |
| 106 static OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) { | 118 static OBJECT_TYPE_INFORMATION QueryObjectTypeInfo(HANDLE handle) { |
| 107 OBJECT_TYPE_INFORMATION info; | 119 OBJECT_TYPE_INFORMATION info; |
| 108 QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL); | 120 QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL); |
| 109 return info; | 121 return info; |
| 110 } | 122 } |
| 111 | 123 |
| 112 /* we're using UTF-8. originally, I had used just the ANSI versions of functions, but that | |
| 113 sucks massive dick. this way we get unicode in the way every single other OS does it */ | |
| 114 static std::string UnicodeStringToUtf8(std::wstring string) { | |
| 115 unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0, NULL, NULL); | |
| 116 std::string ret = std::string(size, '\0'); | |
| 117 ::WideCharToMultiByte(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length(), NULL, NULL); | |
| 118 return ret; | |
| 119 } | |
| 120 | |
| 121 static std::string UnicodeStringToUtf8(UNICODE_STRING string) { | |
| 122 unsigned long size = ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, NULL, 0, NULL, NULL); | |
| 123 std::string ret = std::string(size, '\0'); | |
| 124 ::WideCharToMultiByte(CP_UTF8, 0, string.Buffer, string.Length, &ret.front(), ret.length(), NULL, NULL); | |
| 125 return ret; | |
| 126 } | |
| 127 | |
| 128 static std::wstring Utf8StringToUnicode(std::string string) { | |
| 129 unsigned long size = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), NULL, 0); | |
| 130 std::wstring ret = std::wstring(size, L'\0'); | |
| 131 ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), string.length(), &ret.front(), ret.length()); | |
| 132 return ret; | |
| 133 } | |
| 134 | |
| 135 static std::string GetHandleType(HANDLE handle) { | 124 static std::string GetHandleType(HANDLE handle) { |
| 136 OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle); | 125 OBJECT_TYPE_INFORMATION info = QueryObjectTypeInfo(handle); |
| 137 return UnicodeStringToUtf8(info.TypeName); | 126 return ToUtf8String(info.TypeName); |
| 138 } | 127 } |
| 139 | 128 |
| 140 static std::string GetFinalPathNameByHandle(HANDLE handle) { | 129 static std::string GetFinalPathNameByHandle(HANDLE handle) { |
| 141 std::wstring buffer; | 130 std::wstring buffer; |
| 142 | 131 |
| 143 int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); | 132 int result = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); |
| 144 buffer.resize(result); | 133 buffer.resize(result); |
| 145 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); | 134 ::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); |
| 146 buffer.resize(buffer.find('\0')); | 135 buffer.resize(buffer.find('\0')); |
| 147 | 136 |
| 148 return UnicodeStringToUtf8(buffer); | 137 return ToUtf8String(buffer); |
| 149 } | 138 } |
| 150 | 139 |
| 151 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) { | 140 static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) { |
| 152 if (file_type_index) | 141 if (file_type_index) |
| 153 return object_type_index == file_type_index; | 142 return object_type_index == file_type_index; |
| 168 return false; | 157 return false; |
| 169 | 158 |
| 170 return true; | 159 return true; |
| 171 } | 160 } |
| 172 | 161 |
| 173 static std::string GetSystemDirectory() { | |
| 174 PWSTR path_wch; | |
| 175 SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &path_wch); | |
| 176 std::wstring path_wstr(path_wch); | |
| 177 CoTaskMemFree(path_wch); | |
| 178 return UnicodeStringToUtf8(path_wstr); | |
| 179 } | |
| 180 | |
| 181 static bool IsSystemFile(const std::string& path) { | |
| 182 std::wstring path_w = Utf8StringToUnicode(path); | |
| 183 CharUpperBuffW(&path_w.front(), path_w.length()); | |
| 184 std::wstring windir_w = Utf8StringToUnicode(GetSystemDirectory()); | |
| 185 CharUpperBuffW(&windir_w.front(), windir_w.length()); | |
| 186 return path_w.find(windir_w) == 4; | |
| 187 } | |
| 188 | |
| 189 static bool IsFilePathOk(const std::string& path) { | 162 static bool IsFilePathOk(const std::string& path) { |
| 190 if (path.empty()) | 163 if (path.empty()) |
| 191 return false; | 164 return false; |
| 192 | 165 |
| 193 if (IsSystemFile(path)) | 166 if (IsSystemDirectory(path)) |
| 194 return false; | 167 return false; |
| 195 | 168 |
| 196 const auto file_attributes = GetFileAttributesA(path.c_str()); | 169 const auto file_attributes = GetFileAttributesA(path.c_str()); |
| 197 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY)) | 170 if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY)) |
| 198 return false; | 171 return false; |
| 199 | 172 |
| 200 return true; | 173 return true; |
| 201 } | 174 } |
| 202 | 175 |
| 203 bool Win32FdTools::GetAllPids(std::set<pid_t>& pids) { | 176 bool Win32FdTools::EnumerateOpenProcesses(process_proc_t process_proc) { |
| 204 HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | 177 HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
| 205 if (hProcessSnap == INVALID_HANDLE_VALUE) | 178 if (hProcessSnap == INVALID_HANDLE_VALUE) |
| 206 return false; | 179 return false; |
| 207 | 180 |
| 208 PROCESSENTRY32 pe32; | 181 PROCESSENTRY32 pe32; |
| 209 pe32.dwSize = sizeof(PROCESSENTRY32); | 182 pe32.dwSize = sizeof(PROCESSENTRY32); |
| 210 | 183 |
| 211 if (!::Process32First(hProcessSnap, &pe32)) | 184 if (!::Process32First(hProcessSnap, &pe32)) |
| 212 return false; | 185 return false; |
| 213 | 186 |
| 214 pids.insert(pe32.th32ProcessID); | 187 if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) |
| 188 return false; | |
| 215 | 189 |
| 216 while (::Process32Next(hProcessSnap, &pe32)) | 190 while (::Process32Next(hProcessSnap, &pe32)) |
| 217 pids.insert(pe32.th32ProcessID); | 191 if (!process_proc({pe32.th32ProcessID, pe32.szExeFile})) |
| 192 return false; | |
| 218 | 193 |
| 219 ::CloseHandle(hProcessSnap); | 194 ::CloseHandle(hProcessSnap); |
| 220 | 195 |
| 221 return true; | 196 return true; |
| 222 } | 197 } |
| 223 | 198 |
| 224 bool Win32FdTools::GetProcessName(pid_t pid, std::string& result) { | |
| 225 unsigned long ret_size = 0; // size given by GetModuleBaseNameW | |
| 226 Handle handle(::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); | |
| 227 if (handle.get() == INVALID_HANDLE_VALUE) | |
| 228 return false; | |
| 229 | |
| 230 /* agh... */ | |
| 231 std::wstring ret(256, L'\0'); | |
| 232 for (; ret.length() < 32768; ret.resize(ret.length() * 2)) { | |
| 233 if (!(ret_size = ::GetModuleBaseNameW(handle.get(), 0, &ret.front(), ret.length()))) { | |
| 234 return false; | |
| 235 } else if (ret.length() > ret_size) { | |
| 236 ret.resize(ret.find(L'\0')); | |
| 237 result = UnicodeStringToUtf8(ret); | |
| 238 break; | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 return true; | |
| 243 } | |
| 244 | |
| 245 /* this could be changed to being a callback, but... I'm too lazy right now :) */ | 199 /* this could be changed to being a callback, but... I'm too lazy right now :) */ |
| 246 bool Win32FdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<OpenFile>& files) { | 200 bool Win32FdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) { |
| 201 if (!open_file_proc) | |
| 202 return false; | |
| 203 | |
| 247 std::unordered_map<pid_t, Handle> proc_handles; | 204 std::unordered_map<pid_t, Handle> proc_handles; |
| 248 | 205 |
| 249 for (const pid_t& pid : pids) { | 206 for (const pid_t& pid : pids) { |
| 250 const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); | 207 const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid); |
| 251 if (handle != INVALID_HANDLE_VALUE) | 208 if (handle != INVALID_HANDLE_VALUE) |
| 277 | 234 |
| 278 const std::string path = GetFinalPathNameByHandle(handle.get()); | 235 const std::string path = GetFinalPathNameByHandle(handle.get()); |
| 279 if (!IsFilePathOk(path)) | 236 if (!IsFilePathOk(path)) |
| 280 continue; | 237 continue; |
| 281 | 238 |
| 282 files.push_back({pid, path}); | 239 if (!open_file_proc({pid, path})) |
| 240 return false; | |
| 283 } | 241 } |
| 284 | 242 |
| 285 return true; | 243 return true; |
| 286 } | 244 } |
| 287 | 245 |
