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