diff dep/animone/src/fd/win32.cc @ 258:862d0d8619f6

*: HUUUGE changes animia has been renamed to animone, so instead of thinking of a health condition, you think of a beautiful flower :) I've also edited some of the code for animone, but I have no idea if it even works or not because I don't have a mac or windows machine lying around. whoops! ... anyway, all of the changes divergent from Anisthesia are now licensed under BSD. it's possible that I could even rewrite most of the code to where I don't even have to keep the MIT license, but that's thinking too far into the future I've been slacking off on implementing the anime seasons page, mostly out of laziness. I think I'd have to create another db file specifically for the seasons anyway, this code is being pushed *primarily* because the hard drive it's on is failing! yay :)
author Paper <paper@paper.us.eu.org>
date Mon, 01 Apr 2024 02:43:44 -0400
parents
children 09c5bd74fe93
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/win32.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,259 @@
+#include "animone/fd/win32.h"
+#include "animone.h"
+#include "animone/util/win32.h"
+
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <fileapi.h>
+#include <handleapi.h>
+#include <libloaderapi.h>
+#include <ntdef.h>
+#include <psapi.h>
+#include <shlobj.h>
+#include <stringapiset.h>
+#include <tlhelp32.h>
+#include <windows.h>
+#include <winternl.h>
+
+/* This file is noticably more complex than Unix and Linux, and that's because
+ * there is no "simple" way to get the paths of a file. In fact, this thing requires
+ * you to use *internal functions* that can't even be linked to, hence why we have to
+ * use GetProcAddress and such. What a mess.
+ *
+ * Speaking of which, because this file uses internal functions of the OS, it is not
+ * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista)
+ * at least, so it's unlikely to be changed much ever.
+ */
+
+/* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
+ * 32-bit PIDs, unlike SystemHandleInformation
+ *
+ * TODO: implement SystemHandleInformation for systems older than XP
+ */
+static constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
+static constexpr SYSTEM_INFORMATION_CLASS SystemHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x10);
+static constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004UL;
+
+/* this is filled in at runtime because it's not guaranteed to be (and isn't)
+ * constant between different versions of Windows */
+static unsigned short file_type_index = 0;
+
+struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
+	PVOID Object;
+	ULONG_PTR UniqueProcessId;
+	HANDLE HandleValue;
+	ACCESS_MASK GrantedAccess;
+	USHORT CreatorBackTraceIndex;
+	USHORT ObjectTypeIndex;
+	ULONG HandleAttributes;
+	ULONG Reserved;
+};
+
+struct SYSTEM_HANDLE_INFORMATION_EX {
+	ULONG_PTR NumberOfHandles;
+	ULONG_PTR Reserved;
+	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+};
+
+namespace animone::internal::win32 {
+
+class Ntdll {
+public:
+	Ntdll() {
+		ntdll = ::GetModuleHandleW(L"ntdll.dll");
+		nt_query_system_information = reinterpret_cast<decltype(::NtQuerySystemInformation)*>(
+		    ::GetProcAddress(ntdll, "NtQuerySystemInformation"));
+		nt_query_object = reinterpret_cast<decltype(::NtQueryObject)*>(::GetProcAddress(ntdll, "NtQueryObject"));
+	}
+
+	NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS cls, PVOID sysinfo, ULONG len,
+	                                PULONG retlen){return nt_query_system_information(cls, sysinfo, len, retlen)}
+
+	NTSTATUS QueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS cls, PVOID objinf, ULONG objinflen, PULONG retlen) {
+		return nt_query_object(handle, cls, objinf, objinflen, retlen);
+	}
+
+private:
+	HMODULE ntdll;
+	decltype(::NtQuerySystemInformation)* nt_query_system_information;
+	decltype(::NtQueryObject)* nt_query_object;
+
+}
+
+Ntdll ntdll;
+
+static HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
+	HANDLE dup_handle = nullptr;
+	const bool result =
+	    ::DuplicateHandle(process_handle, handle, ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
+	return result ? dup_handle : nullptr;
+}
+
+static std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetSystemHandleInformation() {
+	/* we should really put a cap on this */
+	ULONG cb = 1 << 19;
+	NTSTATUS status = STATUS_NO_MEMORY;
+	std::unique_ptr<SYSTEM_HANDLE_INFORMATION_EX> info(malloc(cb));
+
+	do {
+		info.reset(malloc(cb *= 2));
+		if (!info)
+			continue;
+
+		status = ntdll.QuerySystemInformation(SystemExtendedHandleInformation, info.get(), cb, &cb);
+	} while (status == STATUS_INFO_LENGTH_MISMATCH);
+
+	if (!NT_SUCCESS(status))
+		return {};
+
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> res;
+
+	ULONG_PTR handles = info->NumberOfHandles;
+	if (!handles)
+		return {};
+
+	res.reserve(handles);
+
+	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* entry = info->Handles;
+	do {
+		if (entry)
+			res.push_back(*(entry++));
+	} while (--handles);
+
+	return res;
+}
+
+static std::wstring GetHandleType(HANDLE handle) {
+	OBJECT_TYPE_INFORMATION info = {0};
+	ntdll.QueryObject(handle, ObjectTypeInformation, &info, sizeof(info), NULL);
+	return std::wstring(info.TypeName.Buffer, info.TypeName.Length);
+}
+
+static std::wstring GetFinalPathNameByHandle(HANDLE handle) {
+	std::wstring buffer;
+
+	DWORD size = ::GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+	buffer.resize(size);
+	::GetFinalPathNameByHandleW(handle, &buffer.front(), buffer.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+
+	return buffer;
+}
+
+static bool IsFileHandle(HANDLE handle, unsigned short object_type_index) {
+	if (file_type_index)
+		return object_type_index == file_type_index;
+	else if (!handle)
+		return true;
+	else if (GetHandleType(handle) == L"File") {
+		file_type_index = object_type_index;
+		return true;
+	}
+	return false;
+}
+
+static bool IsFileMaskOk(ACCESS_MASK access_mask) {
+	if (!(access_mask & FILE_READ_DATA))
+		return false;
+
+	if ((access_mask & FILE_APPEND_DATA) || (access_mask & FILE_WRITE_EA) || (access_mask & FILE_WRITE_ATTRIBUTES))
+		return false;
+
+	return true;
+}
+
+static bool IsFilePathOk(const std::wstring& path) {
+	if (path.empty())
+		return false;
+
+	if (IsSystemDirectory(path))
+		return false;
+
+	const auto file_attributes = GetFileAttributesW(path.c_str());
+	if ((file_attributes == INVALID_FILE_ATTRIBUTES) || (file_attributes & FILE_ATTRIBUTE_DIRECTORY))
+		return false;
+
+	return true;
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	std::wstring wname = GetProcessPath();
+	if (wname.empty())
+		return false;
+
+	return ToUtf8String(GetFileNameWithoutExtension(GetFileNameFromPath(wname)));
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+	if (hProcessSnap == INVALID_HANDLE_VALUE)
+		return false;
+
+	PROCESSENTRY32 pe32;
+	pe32.dwSize = sizeof(PROCESSENTRY32);
+
+	if (!::Process32First(hProcessSnap, &pe32))
+		return false;
+
+	if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+		return false;
+
+	while (::Process32Next(hProcessSnap, &pe32))
+		if (!process_proc({pe32.th32ProcessID, pe32.szExeFile}))
+			return false;
+
+	::CloseHandle(hProcessSnap);
+
+	return true;
+}
+
+/* this could be changed to being a callback, but... I'm too lazy right now :) */
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
+		return false;
+
+	std::unordered_map<pid_t, Handle> proc_handles;
+
+	for (const pid_t& pid : pids) {
+		const HANDLE handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, pid);
+		if (handle != INVALID_HANDLE_VALUE)
+			proc_handles[pid] = Handle(handle);
+	}
+
+	if (proc_handles.empty())
+		return false;
+
+	std::vector<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> info = GetSystemHandleInformation();
+
+	for (const auto& h : info) {
+		const pid_t pid = h.UniqueProcessId;
+		if (!pids.count(pid))
+			continue;
+
+		if (!IsFileHandle(nullptr, h.ObjectTypeIndex))
+			continue;
+
+		if (!IsFileMaskOk(h.GrantedAccess))
+			continue;
+
+		Handle handle(DuplicateHandle(proc_handles[pid].get(), h.HandleValue));
+		if (handle.get() == INVALID_HANDLE_VALUE)
+			continue;
+
+		if (GetFileType(handle.get()) != FILE_TYPE_DISK)
+			continue;
+
+		const std::wstring path = GetFinalPathNameByHandle(handle.get());
+		if (!IsFilePathOk(path))
+			continue;
+
+		if (!open_file_proc({pid, ToUtf8String(path)}))
+			return false;
+	}
+
+	return true;
+}
+
+} // namespace animone::internal::win32