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 |