aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src/usvfs_dll/hookcontext.cpp
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-02-11 02:37:39 -0600
commit7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch)
tree27fb39be241fdb5ac2734c574de678977d1856d0 /libs/usvfs/src/usvfs_dll/hookcontext.cpp
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem, Proton/umu-run integration, and Flatpak packaging. Key features: - FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak) - Proton/GE-Proton/umu-run launcher with env var forwarding - Flatpak support (sandbox-aware VFS, NXM handler, umu-run) - Wine prefix management UI - Case-insensitive path resolution for Linux filesystems - QSettings-safe INI handling (avoids Bethesda INI corruption) - Portable instance support with auto-generated launcher scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/usvfs/src/usvfs_dll/hookcontext.cpp')
-rw-r--r--libs/usvfs/src/usvfs_dll/hookcontext.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/libs/usvfs/src/usvfs_dll/hookcontext.cpp b/libs/usvfs/src/usvfs_dll/hookcontext.cpp
new file mode 100644
index 0000000..f47e42d
--- /dev/null
+++ b/libs/usvfs/src/usvfs_dll/hookcontext.cpp
@@ -0,0 +1,330 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+#include "hookcontext.h"
+#include "exceptionex.h"
+#include "hookcallcontext.h"
+#include "loghelpers.h"
+#include "usvfs.h"
+#include <shared_memory.h>
+#include <sharedparameters.h>
+#include <usvfsparameters.h>
+#include <winapi.h>
+
+namespace bi = boost::interprocess;
+using usvfs::shared::SharedMemoryT;
+using usvfs::shared::VoidAllocatorT;
+
+using namespace usvfs;
+namespace ush = usvfs::shared;
+
+HookContext* HookContext::s_Instance = nullptr;
+
+void printBuffer(const char* buffer, size_t size)
+{
+ static const int bufferSize = 16 * 3;
+ char temp[bufferSize + 1];
+ temp[bufferSize] = '\0';
+
+ for (size_t i = 0; i < size; ++i) {
+ size_t offset = i % 16;
+ _snprintf(&temp[offset * 3], 3, "%02x ", (unsigned char)buffer[i]);
+ if (offset == 15) {
+ spdlog::get("hooks")->info("{0:x} - {1}", i - offset, temp);
+ }
+ }
+
+ spdlog::get("hooks")->info(temp);
+}
+
+HookContext::HookContext(const usvfsParameters& params, HMODULE module)
+ : m_ConfigurationSHM(bi::open_or_create, params.instanceName, 64 * 1024),
+ m_Parameters(retrieveParameters(params)),
+ m_Tree(m_Parameters->currentSHMName(),
+ 4 * 1024 * 1024) // 4 MiB empirically covers most small setups without
+ // need to resize
+ ,
+ m_InverseTree(
+ m_Parameters->currentInverseSHMName(),
+ 128 * 1024) // 128 KiB should cover reverse tree for even larger setups
+ ,
+ m_DLLModule(module)
+{
+ if (s_Instance != nullptr) {
+ throw std::runtime_error("singleton duplicate instantiation (HookContext)");
+ }
+
+ const auto userCount = m_Parameters->userConnected();
+
+ spdlog::get("usvfs")->debug("context current shm: {0} (now {1} connections)",
+ m_Parameters->currentSHMName(), userCount);
+
+ s_Instance = this;
+
+ if (m_Tree.get() == nullptr) {
+ USVFS_THROW_EXCEPTION(usage_error()
+ << ex_msg("shm not found") << ex_msg(params.instanceName));
+ }
+}
+
+void HookContext::remove(const char* instanceName)
+{
+ bi::shared_memory_object::remove(instanceName);
+}
+
+HookContext::~HookContext()
+{
+ spdlog::get("usvfs")->info("releasing hook context");
+
+ s_Instance = nullptr;
+ const auto userCount = m_Parameters->userDisconnected();
+
+ if (userCount == 0) {
+ spdlog::get("usvfs")->info("removing tree {}", m_Parameters->instanceName());
+ bi::shared_memory_object::remove(m_Parameters->instanceName().c_str());
+ } else {
+ spdlog::get("usvfs")->info("{} users left", userCount);
+ }
+}
+
+SharedParameters* HookContext::retrieveParameters(const usvfsParameters& params)
+{
+ std::pair<SharedParameters*, SharedMemoryT::size_type> res =
+ m_ConfigurationSHM.find<SharedParameters>("parameters");
+
+ if (res.first == nullptr) {
+ // not configured yet
+ spdlog::get("usvfs")->info("create config in {}", ::GetCurrentProcessId());
+
+ res.first = m_ConfigurationSHM.construct<SharedParameters>("parameters")(
+ params, VoidAllocatorT(m_ConfigurationSHM.get_segment_manager()));
+
+ if (res.first == nullptr) {
+ USVFS_THROW_EXCEPTION(bi::bad_alloc());
+ }
+ } else {
+ spdlog::get("usvfs")->info("access existing config in {}", ::GetCurrentProcessId());
+ }
+
+ spdlog::get("usvfs")->info("{} processes", res.first->registeredProcessCount());
+
+ return res.first;
+}
+
+HookContext::ConstPtr HookContext::readAccess(const char*)
+{
+ BOOST_ASSERT(s_Instance != nullptr);
+
+ // TODO: this should be a shared mutex!
+ s_Instance->m_Mutex.wait(200);
+ return ConstPtr(s_Instance, unlockShared);
+}
+
+HookContext::Ptr HookContext::writeAccess(const char*)
+{
+ BOOST_ASSERT(s_Instance != nullptr);
+
+ s_Instance->m_Mutex.wait(200);
+ return Ptr(s_Instance, unlock);
+}
+
+void HookContext::setDebugParameters(LogLevel level, CrashDumpsType dumpType,
+ const std::string& dumpPath,
+ std::chrono::milliseconds delayProcess)
+{
+ m_Parameters->setDebugParameters(level, dumpType, dumpPath, delayProcess);
+}
+
+void HookContext::updateParameters() const
+{
+ m_Parameters->setSHMNames(m_Tree.shmName(), m_InverseTree.shmName());
+}
+
+usvfsParameters HookContext::callParameters() const
+{
+ updateParameters();
+ return m_Parameters->makeLocal();
+}
+
+std::wstring HookContext::dllPath() const
+{
+ std::wstring path = winapi::wide::getModuleFileName(m_DLLModule);
+ return boost::filesystem::path(path).parent_path().make_preferred().wstring();
+}
+
+void HookContext::registerProcess(DWORD pid)
+{
+ m_Parameters->registerProcess(pid);
+}
+
+void HookContext::unregisterCurrentProcess()
+{
+ m_Parameters->unregisterProcess(::GetCurrentProcessId());
+}
+
+std::vector<DWORD> HookContext::registeredProcesses() const
+{
+ return m_Parameters->registeredProcesses();
+}
+
+void HookContext::blacklistExecutable(const std::wstring& wexe)
+{
+ const auto exe = shared::string_cast<std::string>(wexe, shared::CodePage::UTF8);
+
+ spdlog::get("usvfs")->debug("blacklisting '{}'", exe);
+ m_Parameters->blacklistExecutable(exe);
+}
+
+void HookContext::clearExecutableBlacklist()
+{
+ spdlog::get("usvfs")->debug("clearing blacklist");
+ m_Parameters->clearExecutableBlacklist();
+}
+
+BOOL HookContext::executableBlacklisted(LPCWSTR wapp, LPCWSTR wcmd) const
+{
+ std::string app;
+ if (wapp) {
+ app = ush::string_cast<std::string>(wapp, ush::CodePage::UTF8);
+ }
+
+ std::string cmd;
+ if (wcmd) {
+ cmd = ush::string_cast<std::string>(wcmd, ush::CodePage::UTF8);
+ }
+
+ return m_Parameters->executableBlacklisted(app, cmd);
+}
+
+void usvfs::HookContext::addSkipFileSuffix(const std::wstring& fileSuffix)
+{
+ const auto fsuffix =
+ shared::string_cast<std::string>(fileSuffix, shared::CodePage::UTF8);
+
+ if (fsuffix.empty()) {
+ return;
+ }
+
+ spdlog::get("usvfs")->debug("added skip file suffix '{}'", fsuffix);
+ m_Parameters->addSkipFileSuffix(fsuffix);
+}
+
+void usvfs::HookContext::clearSkipFileSuffixes()
+{
+ spdlog::get("usvfs")->debug("clearing skip file suffixes");
+ m_Parameters->clearSkipFileSuffixes();
+}
+
+std::vector<std::string> usvfs::HookContext::skipFileSuffixes() const
+{
+ return m_Parameters->skipFileSuffixes();
+}
+
+void usvfs::HookContext::addSkipDirectory(const std::wstring& directory)
+{
+ const auto dir = shared::string_cast<std::string>(directory, shared::CodePage::UTF8);
+
+ if (dir.empty()) {
+ return;
+ }
+
+ spdlog::get("usvfs")->debug("added skip directory '{}'", dir);
+ m_Parameters->addSkipDirectory(dir);
+}
+
+void usvfs::HookContext::clearSkipDirectories()
+{
+ spdlog::get("usvfs")->debug("clearing skip directories");
+ m_Parameters->clearSkipDirectories();
+}
+
+std::vector<std::string> usvfs::HookContext::skipDirectories() const
+{
+ return m_Parameters->skipDirectories();
+}
+
+void HookContext::forceLoadLibrary(const std::wstring& wprocess,
+ const std::wstring& wpath)
+{
+ const auto process =
+ shared::string_cast<std::string>(wprocess, shared::CodePage::UTF8);
+
+ const auto path = shared::string_cast<std::string>(wpath, shared::CodePage::UTF8);
+
+ spdlog::get("usvfs")->debug("adding forced library '{}' for process '{}'", path,
+ process);
+
+ m_Parameters->addForcedLibrary(process, path);
+}
+
+void HookContext::clearLibraryForceLoads()
+{
+ spdlog::get("usvfs")->debug("clearing forced libraries");
+ m_Parameters->clearForcedLibraries();
+}
+
+std::vector<std::wstring>
+HookContext::librariesToForceLoad(const std::wstring& processName)
+{
+ const auto v = m_Parameters->forcedLibraries(
+ shared::string_cast<std::string>(processName, shared::CodePage::UTF8));
+
+ std::vector<std::wstring> wv;
+ for (const auto& s : v) {
+ wv.push_back(shared::string_cast<std::wstring>(s, shared::CodePage::UTF8));
+ }
+
+ return wv;
+}
+
+void HookContext::registerDelayed(std::future<int> delayed)
+{
+ m_Futures.push_back(std::move(delayed));
+}
+
+std::vector<std::future<int>>& HookContext::delayed()
+{
+ return m_Futures;
+}
+
+void HookContext::unlock(HookContext* instance)
+{
+ instance->m_Mutex.signal();
+}
+
+void HookContext::unlockShared(const HookContext* instance)
+{
+ instance->m_Mutex.signal();
+}
+
+// deprecated
+//
+extern "C" DLLEXPORT HookContext* __cdecl CreateHookContext(
+ const USVFSParameters& oldParams, HMODULE module)
+{
+ const usvfsParameters p(oldParams);
+ return usvfsCreateHookContext(p, module);
+}
+
+extern "C" DLLEXPORT usvfs::HookContext* WINAPI
+usvfsCreateHookContext(const usvfsParameters& params, HMODULE module)
+{
+ return new HookContext(params, module);
+}