aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src/usvfs_dll
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-12 02:40:16 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-12 02:40:16 -0500
commitbf7a57fc55493247a624ecfd65e4d73964e7d8d2 (patch)
tree0a4b2d6123672768187a5ab863e63cd3f21bb319 /libs/usvfs/src/usvfs_dll
parente0fb66428a9b78b4c326df8d7e30429a5b271d74 (diff)
Remove vendored usvfs and Windows-only usvfs code paths
The Linux port uses its own in-process FUSE VFS (FuseConnector), so usvfs was dead weight: libs/usvfs/ was never built, usvfsconnector.cpp was explicitly REMOVE_ITEM'd from the source list, and every call site was already guarded by #ifdef _WIN32 with a working Linux branch (the Windows build is also broken on this branch, since FUSE3 is a hard find_package dependency). - Drop libs/usvfs/ and src/src/usvfsconnector.{cpp,h} - Collapse #ifdef _WIN32 / usvfs include branches in organizercore, mainwindow, settings, util, spawn - Remove getUsvfsVersionString() and the "usvfs: ..." log field - Remove vfs32/64DLLName APPPARAMs (nothing reads them) - Relabel About dialog "usvfs:" row to "VFS: FUSE 3"
Diffstat (limited to 'libs/usvfs/src/usvfs_dll')
-rw-r--r--libs/usvfs/src/usvfs_dll/CMakeLists.txt57
-rw-r--r--libs/usvfs/src/usvfs_dll/hookcallcontext.cpp112
-rw-r--r--libs/usvfs/src/usvfs_dll/hookcallcontext.h87
-rw-r--r--libs/usvfs/src/usvfs_dll/hookcontext.cpp330
-rw-r--r--libs/usvfs/src/usvfs_dll/hookcontext.h224
-rw-r--r--libs/usvfs/src/usvfs_dll/hookmanager.cpp333
-rw-r--r--libs/usvfs/src/usvfs_dll/hookmanager.h81
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h263
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp1860
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/kernel32.h116
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp1520
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/ntdll.h57
-rw-r--r--libs/usvfs/src/usvfs_dll/hooks/sharedids.h9
-rw-r--r--libs/usvfs/src/usvfs_dll/maptracker.h691
-rw-r--r--libs/usvfs/src/usvfs_dll/pch.cpp1
-rw-r--r--libs/usvfs/src/usvfs_dll/redirectiontree.cpp27
-rw-r--r--libs/usvfs/src/usvfs_dll/redirectiontree.h104
-rw-r--r--libs/usvfs/src/usvfs_dll/semaphore.cpp57
-rw-r--r--libs/usvfs/src/usvfs_dll/semaphore.h27
-rw-r--r--libs/usvfs/src/usvfs_dll/sharedparameters.cpp264
-rw-r--r--libs/usvfs/src/usvfs_dll/stringcast_boost.h42
-rw-r--r--libs/usvfs/src/usvfs_dll/usvfs.cpp965
-rw-r--r--libs/usvfs/src/usvfs_dll/usvfsparameters.cpp193
-rw-r--r--libs/usvfs/src/usvfs_dll/version.rc40
24 files changed, 0 insertions, 7460 deletions
diff --git a/libs/usvfs/src/usvfs_dll/CMakeLists.txt b/libs/usvfs/src/usvfs_dll/CMakeLists.txt
deleted file mode 100644
index 2520b50..0000000
--- a/libs/usvfs/src/usvfs_dll/CMakeLists.txt
+++ /dev/null
@@ -1,57 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-include(GNUInstallDirs)
-
-find_package(Boost CONFIG REQUIRED COMPONENTS thread)
-find_package(spdlog CONFIG REQUIRED)
-
-file(GLOB sources "*.cpp" "*.h")
-source_group("" FILES ${sources})
-
-file(GLOB hooks "hooks/*.cpp" "hooks/*.h")
-source_group("dlls" FILES ${hooks})
-
-add_library(usvfs_dll SHARED)
-target_sources(usvfs_dll
- PRIVATE
- ${PROJECT_SOURCE_DIR}/include/usvfs/usvfsparametersprivate.h
- ${sources}
- ${hooks}
- version.rc
- PUBLIC
- FILE_SET HEADERS
- BASE_DIRS ${PROJECT_SOURCE_DIR}/include
- FILES
- ${PROJECT_SOURCE_DIR}/include/usvfs/dllimport.h
- ${PROJECT_SOURCE_DIR}/include/usvfs/logging.h
- ${PROJECT_SOURCE_DIR}/include/usvfs/sharedparameters.h
- ${PROJECT_SOURCE_DIR}/include/usvfs/usvfs_version.h
- ${PROJECT_SOURCE_DIR}/include/usvfs/usvfs.h
- ${PROJECT_SOURCE_DIR}/include/usvfs/usvfsparameters.h
-)
-target_link_libraries(usvfs_dll
- PRIVATE shared thooklib usvfs_helper
- Boost::thread spdlog::spdlog_header_only
- Shlwapi)
-target_compile_definitions(usvfs_dll PRIVATE BUILDING_USVFS_DLL)
-set_target_properties(usvfs_dll
- PROPERTIES
- ARCHIVE_OUTPUT_NAME usvfs${ARCH_POSTFIX}
- ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR}
- ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR}
- LIBRARY_OUTPUT_NAME usvfs${ARCH_POSTFIX}
- LIBRARY_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR}
- LIBRARY_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR}
- PDB_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR}
- PDB_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR}
- RUNTIME_OUTPUT_NAME usvfs${ARCH_POSTFIX}
- RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR}
- RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR})
-
-install(TARGETS usvfs_dll EXPORT usvfs${ARCH_POSTFIX}Targets FILE_SET HEADERS)
-install(FILES $<TARGET_PDB_FILE:usvfs_dll> DESTINATION pdb OPTIONAL)
-install(EXPORT usvfs${ARCH_POSTFIX}Targets
- FILE usvfs${ARCH_POSTFIX}Targets.cmake
- NAMESPACE usvfs${ARCH_POSTFIX}::
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/usvfs
-)
diff --git a/libs/usvfs/src/usvfs_dll/hookcallcontext.cpp b/libs/usvfs/src/usvfs_dll/hookcallcontext.cpp
deleted file mode 100644
index 32510c9..0000000
--- a/libs/usvfs/src/usvfs_dll/hookcallcontext.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-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 "hookcallcontext.h"
-#include "hookcontext.h"
-#include <logging.h>
-
-namespace usvfs
-{
-
-class HookStack
-{
-public:
- static HookStack& instance()
- {
- PreserveGetLastError boostChangesGetLastError;
- if (s_Instance.get() == nullptr) {
- s_Instance.reset(new HookStack());
- }
- return *s_Instance.get();
- }
-
- bool setGroup(MutExHookGroup group)
- {
- if (m_ActiveGroups.test(static_cast<size_t>(group)) ||
- m_ActiveGroups.test(static_cast<size_t>(MutExHookGroup::ALL_GROUPS))) {
- return false;
- } else {
- m_ActiveGroups.set(static_cast<size_t>(group), true);
- return true;
- }
- }
-
- void unsetGroup(MutExHookGroup group)
- {
- m_ActiveGroups.set(static_cast<size_t>(group), false);
- }
-
-private:
- HookStack() {}
-
-private:
- static boost::thread_specific_ptr<HookStack> s_Instance;
- std::bitset<static_cast<size_t>(MutExHookGroup::LAST)> m_ActiveGroups;
-};
-
-boost::thread_specific_ptr<HookStack> HookStack::s_Instance;
-
-HookCallContext::HookCallContext() : m_Active(true), m_Group(MutExHookGroup::NO_GROUP)
-{
- updateLastError();
-}
-
-HookCallContext::HookCallContext(MutExHookGroup group)
- : m_Active(HookStack::instance().setGroup(group)), m_Group(group)
-{
- updateLastError();
-}
-
-HookCallContext::~HookCallContext()
-{
- if (m_Active && (m_Group != MutExHookGroup::NO_GROUP)) {
- HookStack::instance().unsetGroup(m_Group);
- }
- SetLastError(m_LastError);
-}
-
-void HookCallContext::restoreLastError()
-{
- SetLastError(m_LastError);
-}
-
-void HookCallContext::updateLastError(DWORD lastError)
-{
- m_LastError = lastError;
-}
-
-bool HookCallContext::active() const
-{
- return m_Active;
-}
-
-FunctionGroupLock::FunctionGroupLock(MutExHookGroup group) : m_Group(group)
-{
- m_Active = HookStack::instance().setGroup(m_Group);
-}
-
-FunctionGroupLock::~FunctionGroupLock()
-{
- if (m_Active) {
- HookStack::instance().unsetGroup(m_Group);
- }
-}
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hookcallcontext.h b/libs/usvfs/src/usvfs_dll/hookcallcontext.h
deleted file mode 100644
index ebb3bf9..0000000
--- a/libs/usvfs/src/usvfs_dll/hookcallcontext.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-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/>.
-*/
-#pragma once
-
-namespace usvfs
-{
-
-/**
- * @brief groups of hooks which may be used to implement each other, so only the first
- * call should be to one should be manipulated
- */
-enum class MutExHookGroup : int
-{
- ALL_GROUPS = 0, // An ALL_GROUPS-hook prevents all other hooks from becoming active
- // BUT hooks from other groups don't prevent the ALL_GROUPS-hook from
- // becoming activated
- OPEN_FILE = 1,
- CREATE_PROCESS = 2,
- FILE_ATTRIBUTES = 3,
- FIND_FILES = 4,
- LOAD_LIBRARY = 5,
- FULL_PATHNAME = 6,
- SHELL_FILEOP = 7,
- DELETE_FILE = 8,
- GET_FILE_VERSION = 9,
- GET_MODULE_HANDLE = 10,
- SEARCH_FILES = 11,
-
- NO_GROUP = 12,
- LAST = NO_GROUP,
-};
-
-class HookCallContext
-{
-
-public:
- HookCallContext();
- HookCallContext(MutExHookGroup group);
- ~HookCallContext();
-
- HookCallContext(const HookCallContext& reference) = delete;
- HookCallContext& operator=(const HookCallContext& reference) = delete;
-
- void restoreLastError();
-
- void updateLastError(DWORD lastError = GetLastError());
-
- DWORD lastError() const { return m_LastError; }
-
- bool active() const;
-
-private:
- DWORD m_LastError;
- bool m_Active;
- MutExHookGroup m_Group;
-};
-
-class FunctionGroupLock
-{
-public:
- FunctionGroupLock(MutExHookGroup group);
- ~FunctionGroupLock();
-
-private:
- MutExHookGroup m_Group;
- bool m_Active;
-};
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hookcontext.cpp b/libs/usvfs/src/usvfs_dll/hookcontext.cpp
deleted file mode 100644
index f47e42d..0000000
--- a/libs/usvfs/src/usvfs_dll/hookcontext.cpp
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
-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);
-}
diff --git a/libs/usvfs/src/usvfs_dll/hookcontext.h b/libs/usvfs/src/usvfs_dll/hookcontext.h
deleted file mode 100644
index c9f6eb1..0000000
--- a/libs/usvfs/src/usvfs_dll/hookcontext.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-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/>.
-*/
-#pragma once
-
-#include "dllimport.h"
-#include "redirectiontree.h"
-#include "semaphore.h"
-#include "tree_container.h"
-#include <directory_tree.h>
-#include <exceptionex.h>
-#include <usvfsparameters.h>
-#include <usvfsparametersprivate.h>
-#include <winapi.h>
-
-namespace usvfs
-{
-
-class DLLEXPORT SharedParameters;
-
-/**
- * @brief context available to hooks. This is protected by a many-reader
- * single-writer mutex
- */
-class HookContext
-{
-
-public:
- typedef std::unique_ptr<const HookContext, void (*)(const HookContext*)> ConstPtr;
- typedef std::unique_ptr<HookContext, void (*)(HookContext*)> Ptr;
- typedef unsigned int DataIDT;
-
-public:
- HookContext(const usvfsParameters& params, HMODULE module);
-
- HookContext(const HookContext& reference) = delete;
-
- DLLEXPORT ~HookContext();
-
- HookContext& operator=(const HookContext& reference) = delete;
-
- static void remove(const char* instance);
-
- /**
- * @brief get read access to the context.
- * @return smart ptr to the context. mutex will automatically be released when
- * this leaves scope
- */
- static ConstPtr readAccess(const char* source);
-
- /**
- * @brief get write access to the context.
- * @return smart ptr to the context. mutex will automatically be released when
- * this leaves scope
- */
- static Ptr writeAccess(const char* source);
-
- /**
- * @return table containing file redirection information
- */
- RedirectionTreeContainer& redirectionTable() { return m_Tree; }
-
- /**
- * @return table containing file redirection information
- */
- const RedirectionTreeContainer& redirectionTable() const { return m_Tree; }
-
- RedirectionTreeContainer& inverseTable() { return m_InverseTree; }
-
- const RedirectionTreeContainer& inverseTable() const { return m_InverseTree; }
-
- /**
- * @return the parameters passed in on dll initialisation
- */
- usvfsParameters callParameters() const;
-
- /**
- * @return path to the calling library itself
- */
- std::wstring dllPath() const;
-
- /**
- * @brief get access to custom data
- * @note the caller gains write access to the data, independent on the lock on
- * the context
- * as a whole. The caller himself has to ensure thread safety
- */
- template <typename T>
- T& customData(DataIDT id) const
- {
- auto iter = m_CustomData.find(id);
- if (iter == m_CustomData.end()) {
- iter = m_CustomData.insert(std::make_pair(id, T())).first;
- }
- // std::map is supposed to not invalidate any iterators when elements are
- // added
- // so it should be safe to return a pointer here
- T* res = boost::any_cast<T>(&iter->second);
- return *res;
- }
-
- void registerProcess(DWORD pid);
- void unregisterCurrentProcess();
- std::vector<DWORD> registeredProcesses() const;
-
- void blacklistExecutable(const std::wstring& executableName);
- void clearExecutableBlacklist();
- BOOL executableBlacklisted(LPCWSTR lpApplicationName, LPCWSTR lpCommandLine) const;
-
- void addSkipFileSuffix(const std::wstring& fileSuffix);
- void clearSkipFileSuffixes();
- std::vector<std::string> skipFileSuffixes() const;
-
- void addSkipDirectory(const std::wstring& directory);
- void clearSkipDirectories();
- std::vector<std::string> skipDirectories() const;
-
- void forceLoadLibrary(const std::wstring& processName,
- const std::wstring& libraryPath);
- void clearLibraryForceLoads();
- std::vector<std::wstring> librariesToForceLoad(const std::wstring& processName);
-
- void setDebugParameters(LogLevel level, CrashDumpsType dumpType,
- const std::string& dumpPath,
- std::chrono::milliseconds delayProcess);
-
- void updateParameters() const;
-
- void registerDelayed(std::future<int> delayed);
-
- std::vector<std::future<int>>& delayed();
-
-private:
- static void unlock(HookContext* instance);
- static void unlockShared(const HookContext* instance);
-
- SharedParameters* retrieveParameters(const usvfsParameters& params);
-
-private:
- static HookContext* s_Instance;
-
- shared::SharedMemoryT m_ConfigurationSHM;
- SharedParameters* m_Parameters{nullptr};
- RedirectionTreeContainer m_Tree;
- RedirectionTreeContainer m_InverseTree;
-
- std::vector<std::future<int>> m_Futures;
-
- mutable std::map<DataIDT, boost::any> m_CustomData;
-
- HMODULE m_DLLModule;
-
- // mutable std::recursive_mutex m_Mutex;
- mutable RecursiveBenaphore m_Mutex;
-};
-
-} // namespace usvfs
-
-extern "C" DLLEXPORT usvfs::HookContext* WINAPI
-usvfsCreateHookContext(const usvfsParameters& params, HMODULE module);
-
-class PreserveGetLastError
-{
-public:
- PreserveGetLastError() : m_err(GetLastError()) {}
- ~PreserveGetLastError() { SetLastError(m_err); }
-
-private:
- DWORD m_err;
-};
-
-// declare an identifier that is guaranteed to be unique across the application
-#define DATA_ID(name) static const usvfs::HookContext::DataIDT name = __COUNTER__
-
-// set of macros. These ensure a call context is created but most of all these
-// ensure exceptions are caught.
-
-#define READ_CONTEXT() HookContext::readAccess(__MYFUNC__)
-#define WRITE_CONTEXT() HookContext::writeAccess(__MYFUNC__)
-
-#define HOOK_START_GROUP(group) \
- try { \
- HookCallContext callContext(group);
-
-#define HOOK_START \
- try { \
- HookCallContext callContext;
-
-#define HOOK_END \
- } \
- catch (const std::exception& e) \
- { \
- spdlog::get("usvfs")->error("exception in {0}: {1}", __MYFUNC__, e.what()); \
- logExtInfo(e); \
- }
-
-#define HOOK_ENDP(param) \
- } \
- catch (const std::exception& e) \
- { \
- spdlog::get("usvfs")->error("exception in {0} ({1}): {2}", __MYFUNC__, param, \
- e.what()); \
- logExtInfo(e); \
- }
-
-#define PRE_REALCALL callContext.restoreLastError();
-#define POST_REALCALL callContext.updateLastError();
diff --git a/libs/usvfs/src/usvfs_dll/hookmanager.cpp b/libs/usvfs/src/usvfs_dll/hookmanager.cpp
deleted file mode 100644
index 731b856..0000000
--- a/libs/usvfs/src/usvfs_dll/hookmanager.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
-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 "hookmanager.h"
-#include "../thooklib/ttrampolinepool.h"
-#include "../thooklib/utility.h"
-#include "exceptionex.h"
-#include "hooks/kernel32.h"
-#include "hooks/ntdll.h"
-#include "usvfs.h"
-#include <VersionHelpers.h>
-#include <directory_tree.h>
-#include <logging.h>
-#include <shmlogger.h>
-#include <usvfsparameters.h>
-#include <winapi.h>
-
-using namespace HookLib;
-namespace bf = boost::filesystem;
-
-namespace usvfs
-{
-
-HookManager* HookManager::s_Instance = nullptr;
-
-HookManager::HookManager(const usvfsParameters& params, HMODULE module)
- : m_Context(params, module)
-{
- if (s_Instance != nullptr) {
- throw std::runtime_error("singleton duplicate instantiation (HookManager)");
- }
-
- s_Instance = this;
-
- m_Context.registerProcess(::GetCurrentProcessId());
- spdlog::get("usvfs")->info("Process registered in shared process list : {}",
- ::GetCurrentProcessId());
-
- winapi::ex::OSVersion version = winapi::ex::getOSVersion();
- spdlog::get("usvfs")->info(
- "Windows version {}.{}.{} sp {} platform {} ({})", version.major, version.minor,
- version.build, version.servicpack, version.platformid,
- shared::string_cast<std::string>(winapi::ex::wide::getWindowsBuildLab(true))
- .c_str());
-
- initHooks();
-
- if (params.debugMode) {
- while (!::IsDebuggerPresent()) {
- // wait for debugger to attach
- ::Sleep(100);
- }
- }
-}
-
-HookManager::~HookManager()
-{
- spdlog::get("hooks")->debug("end hook of process {}", GetCurrentProcessId());
- removeHooks();
- m_Context.unregisterCurrentProcess();
-}
-
-HookManager& HookManager::instance()
-{
- if (s_Instance == nullptr) {
- throw std::runtime_error("singleton not instantiated");
- }
-
- return *s_Instance;
-}
-
-LPVOID HookManager::detour(const char* functionName)
-{
- auto iter = m_Hooks.find(functionName);
- if (iter != m_Hooks.end()) {
- return GetDetour(iter->second);
- } else {
- return nullptr;
- }
-}
-
-void HookManager::removeHook(const std::string& functionName)
-{
- auto iter = m_Hooks.find(functionName);
- if (iter != m_Hooks.end()) {
- try {
- RemoveHook(iter->second);
- m_Hooks.erase(iter);
- spdlog::get("usvfs")->info("removed hook for {}", functionName);
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->critical("failed to remove hook of {}: {}", functionName,
- e.what());
- }
- } else {
- spdlog::get("usvfs")->info("{} wasn't hooked", functionName);
- }
-}
-
-void HookManager::logStubInt(LPVOID address)
-{
- if (m_Stubs.find(address) != m_Stubs.end()) {
- spdlog::get("hooks")->warn("{0} called", m_Stubs[address]);
- } else {
- spdlog::get("hooks")->warn("unknown function at {0} called", address);
- }
-}
-
-void HookManager::logStub(LPVOID address)
-{
- try {
- instance().logStubInt(address);
- } catch (const std::exception& e) {
- spdlog::get("hooks")->debug("function at {0} called after shutdown: {1}", address,
- e.what());
- }
-}
-
-void HookManager::installHook(HMODULE module1, HMODULE module2,
- const std::string& functionName, LPVOID hook,
- LPVOID* fillFuncAddr = nullptr)
-{
- BOOST_ASSERT(hook != nullptr);
- HOOKHANDLE handle = INVALID_HOOK;
- HookError err = ERR_NONE;
- LPVOID funcAddr = nullptr;
- HMODULE usedModule = nullptr;
- // both module1 and module2 are allowed to be null
- if (module1 != nullptr) {
- funcAddr = MyGetProcAddress(module1, functionName.c_str());
- if (funcAddr != nullptr) {
- handle = InstallHook(funcAddr, hook, &err);
- }
- if (handle != INVALID_HOOK)
- usedModule = module1;
- }
-
- if ((handle == INVALID_HOOK) && (module2 != nullptr)) {
- funcAddr = MyGetProcAddress(module2, functionName.c_str());
- if (funcAddr != nullptr) {
- handle = InstallHook(funcAddr, hook, &err);
- }
- if (handle != INVALID_HOOK)
- usedModule = module2;
- }
-
- if (fillFuncAddr)
- *fillFuncAddr = funcAddr;
-
- if (handle == INVALID_HOOK) {
- spdlog::get("usvfs")->error("failed to hook {0}: {1}", functionName,
- GetErrorString(err));
- } else {
- m_Stubs.insert(make_pair(funcAddr, functionName));
- m_Hooks.insert(make_pair(std::string(functionName), handle));
- spdlog::get("usvfs")->info("hooked {0} ({1}) in {2} type {3}", functionName,
- funcAddr, winapi::ansi::getModuleFileName(usedModule),
- GetHookType(handle));
- }
-}
-
-void HookManager::installStub(HMODULE module1, HMODULE module2,
- const std::string& functionName)
-{
- HOOKHANDLE handle = INVALID_HOOK;
- HookError err = ERR_NONE;
- LPVOID funcAddr = nullptr;
- HMODULE usedModule = nullptr;
- // both module1 and module2 are allowed to be null
- if (module1 != nullptr) {
- funcAddr = MyGetProcAddress(module1, functionName.c_str());
- if (funcAddr != nullptr) {
- handle = InstallStub(funcAddr, logStub, &err);
- } else {
- spdlog::get("usvfs")->debug("{} doesn't contain {}",
- winapi::ansi::getModuleFileName(module1),
- functionName);
- }
- if (handle != INVALID_HOOK)
- usedModule = module1;
- }
-
- if ((handle == INVALID_HOOK) && (module2 != nullptr)) {
- funcAddr = MyGetProcAddress(module2, functionName.c_str());
- if (funcAddr != nullptr) {
- handle = InstallStub(funcAddr, logStub, &err);
- } else {
- spdlog::get("usvfs")->debug("{} doesn't contain {}",
- winapi::ansi::getModuleFileName(module2),
- functionName);
- }
- if (handle != INVALID_HOOK)
- usedModule = module2;
- }
-
- if (handle == INVALID_HOOK) {
- spdlog::get("usvfs")->error("failed to stub {0}: {1}", functionName,
- GetErrorString(err));
- } else {
- m_Stubs.insert(make_pair(funcAddr, functionName));
- m_Hooks.insert(make_pair(std::string(functionName), handle));
- spdlog::get("usvfs")->info("stubbed {0} ({1}) in {2} type {3}", functionName,
- funcAddr, winapi::ansi::getModuleFileName(usedModule),
- GetHookType(handle));
- }
-}
-
-void HookManager::initHooks()
-{
- TrampolinePool::initialize();
-
- HookLib::TrampolinePool::instance().setBlock(true);
-
- HMODULE k32Mod = GetModuleHandleA("kernel32.dll");
- spdlog::get("usvfs")->debug("kernel32.dll at {0:x}",
- reinterpret_cast<uintptr_t>(k32Mod));
- // kernelbase.dll contains the actual implementation for functions formerly in
- // kernel32.dll and advapi32.dll, starting with Windows 7
- // http://msdn.microsoft.com/en-us/library/windows/desktop/dd371752(v=vs.85).aspx
- HMODULE kbaseMod = GetModuleHandleA("kernelbase.dll");
- spdlog::get("usvfs")->debug("kernelbase.dll at {0:x}",
- reinterpret_cast<uintptr_t>(kbaseMod));
-
- installHook(kbaseMod, k32Mod, "GetFileAttributesExA", hook_GetFileAttributesExA);
- installHook(kbaseMod, k32Mod, "GetFileAttributesA", hook_GetFileAttributesA);
- installHook(kbaseMod, k32Mod, "GetFileAttributesExW", hook_GetFileAttributesExW);
- installHook(kbaseMod, k32Mod, "GetFileAttributesW", hook_GetFileAttributesW);
- installHook(kbaseMod, k32Mod, "SetFileAttributesW", hook_SetFileAttributesW);
-
- installHook(kbaseMod, k32Mod, "CreateDirectoryW", hook_CreateDirectoryW);
- installHook(kbaseMod, k32Mod, "RemoveDirectoryW", hook_RemoveDirectoryW);
- installHook(kbaseMod, k32Mod, "DeleteFileW", hook_DeleteFileW);
- installHook(kbaseMod, k32Mod, "GetCurrentDirectoryA", hook_GetCurrentDirectoryA);
- installHook(kbaseMod, k32Mod, "GetCurrentDirectoryW", hook_GetCurrentDirectoryW);
- installHook(kbaseMod, k32Mod, "SetCurrentDirectoryA", hook_SetCurrentDirectoryA);
- installHook(kbaseMod, k32Mod, "SetCurrentDirectoryW", hook_SetCurrentDirectoryW);
-
- installHook(kbaseMod, k32Mod, "ExitProcess", hook_ExitProcess);
-
- installHook(kbaseMod, k32Mod, "CreateProcessInternalW", hook_CreateProcessInternalW,
- reinterpret_cast<LPVOID*>(&CreateProcessInternalW));
-
- installHook(kbaseMod, k32Mod, "MoveFileA", hook_MoveFileA);
- installHook(kbaseMod, k32Mod, "MoveFileW", hook_MoveFileW);
- installHook(kbaseMod, k32Mod, "MoveFileExA", hook_MoveFileExA);
- installHook(kbaseMod, k32Mod, "MoveFileExW", hook_MoveFileExW);
- installHook(kbaseMod, k32Mod, "MoveFileWithProgressA", hook_MoveFileWithProgressA);
- installHook(kbaseMod, k32Mod, "MoveFileWithProgressW", hook_MoveFileWithProgressW);
-
- installHook(kbaseMod, k32Mod, "CopyFileExW", hook_CopyFileExW);
- if (IsWindows8OrGreater())
- installHook(kbaseMod, k32Mod, "CopyFile2", hook_CopyFile2,
- reinterpret_cast<LPVOID*>(&CopyFile2));
-
- installHook(kbaseMod, k32Mod, "GetPrivateProfileStringA",
- hook_GetPrivateProfileStringA);
- installHook(kbaseMod, k32Mod, "GetPrivateProfileStringW",
- hook_GetPrivateProfileStringW);
- installHook(kbaseMod, k32Mod, "GetPrivateProfileSectionA",
- hook_GetPrivateProfileSectionA);
- installHook(kbaseMod, k32Mod, "GetPrivateProfileSectionW",
- hook_GetPrivateProfileSectionW);
- installHook(kbaseMod, k32Mod, "WritePrivateProfileStringA",
- hook_WritePrivateProfileStringA);
- installHook(kbaseMod, k32Mod, "WritePrivateProfileStringW",
- hook_WritePrivateProfileStringW);
-
- installHook(kbaseMod, k32Mod, "GetFullPathNameA", hook_GetFullPathNameA);
- installHook(kbaseMod, k32Mod, "GetFullPathNameW", hook_GetFullPathNameW);
-
- installHook(kbaseMod, k32Mod, "FindFirstFileExW", hook_FindFirstFileExW);
-
- HMODULE ntdllMod = GetModuleHandleA("ntdll.dll");
- spdlog::get("usvfs")->debug("ntdll.dll at {0:x}",
- reinterpret_cast<uintptr_t>(ntdllMod));
- installHook(ntdllMod, nullptr, "NtQueryFullAttributesFile",
- hook_NtQueryFullAttributesFile);
- installHook(ntdllMod, nullptr, "NtQueryAttributesFile", hook_NtQueryAttributesFile);
- installHook(ntdllMod, nullptr, "NtQueryDirectoryFile", hook_NtQueryDirectoryFile);
- installHook(ntdllMod, nullptr, "NtQueryDirectoryFileEx", hook_NtQueryDirectoryFileEx);
- installHook(ntdllMod, nullptr, "NtQueryObject", hook_NtQueryObject);
- installHook(ntdllMod, nullptr, "NtQueryInformationFile", hook_NtQueryInformationFile);
- installHook(ntdllMod, nullptr, "NtQueryInformationByName",
- hook_NtQueryInformationByName);
- installHook(ntdllMod, nullptr, "NtOpenFile", hook_NtOpenFile);
- installHook(ntdllMod, nullptr, "NtCreateFile", hook_NtCreateFile);
- installHook(ntdllMod, nullptr, "NtClose", hook_NtClose);
- installHook(ntdllMod, nullptr, "NtTerminateProcess", hook_NtTerminateProcess);
-
- installHook(kbaseMod, k32Mod, "LoadLibraryExA", hook_LoadLibraryExA);
- installHook(kbaseMod, k32Mod, "LoadLibraryExW", hook_LoadLibraryExW);
-
- // install this hook late as usvfs is calling it itself for debugging purposes
- installHook(kbaseMod, k32Mod, "GetModuleFileNameA", hook_GetModuleFileNameA);
- installHook(kbaseMod, k32Mod, "GetModuleFileNameW", hook_GetModuleFileNameW);
-
- spdlog::get("usvfs")->debug("hooks installed");
- HookLib::TrampolinePool::instance().setBlock(false);
-}
-
-void HookManager::removeHooks()
-{
- while (m_Hooks.size() > 0) {
- auto iter = m_Hooks.begin();
- try {
- RemoveHook(iter->second);
- spdlog::get("usvfs")->debug("removed hook {}", iter->first);
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->critical("failed to remove hook: {}", e.what());
- }
-
- // remove either way, otherwise this is an endless loop
- m_Hooks.erase(iter);
- }
-}
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hookmanager.h b/libs/usvfs/src/usvfs_dll/hookmanager.h
deleted file mode 100644
index 636a29c..0000000
--- a/libs/usvfs/src/usvfs_dll/hookmanager.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-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/>.
-*/
-#pragma once
-
-#include "hookcontext.h"
-#include <hooklib.h>
-#include <usvfsparameters.h>
-
-namespace usvfs
-{
-
-class HookManager
-{
-public:
- HookManager(const usvfsParameters& params, HMODULE module);
- ~HookManager();
-
- HookManager(const HookManager& reference) = delete;
-
- HookManager& operator=(const HookManager& reference) = delete;
-
- static HookManager& instance();
-
- HookContext* context() { return &m_Context; }
-
- ///
- /// \brief retrieve address of the detour of a function
- /// \param functionName name of the function to look up
- /// \return function address that can be used to directly execute the original code
- ///
- LPVOID detour(const char* functionName);
-
- ///
- /// \brief remove the hook on the specified function.
- /// \param functionName name of the function to unhook
- /// \note This function is only exposed to allow a workaround for ExitProcess and may
- /// be
- /// removed if a better solution is found there. If you have another legit use
- /// case, please let me know!
- ///
- void removeHook(const std::string& functionName);
-
-private:
- void logStubInt(LPVOID address);
- static void logStub(LPVOID address);
-
- void installHook(HMODULE module1, HMODULE module2, const std::string& functionName,
- LPVOID hook, LPVOID* fillFuncAddr);
- void installStub(HMODULE module1, HMODULE module2, const std::string& functionName);
- void initHooks();
- void removeHooks();
-
-private:
- static HookManager* s_Instance;
-
- std::map<std::string, HookLib::HOOKHANDLE> m_Hooks;
-
- std::map<LPVOID, std::string> m_Stubs;
-
- HookContext m_Context;
-};
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h b/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h
deleted file mode 100644
index dbd3597..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/file_information_utils.h
+++ /dev/null
@@ -1,263 +0,0 @@
-#pragma once
-
-/**
- * This header defines function to deal with the various FileInformationClass
- * enumeration.
- */
-
-#include <type_traits>
-
-#include <ntdll_declarations.h>
-
-#include <windows.h>
-
-namespace usvfs::details
-{
-
-#define DECLARE_HAS_FIELD(Field) \
- template <typename T> \
- constexpr auto HasFieldImpl##Field(int)->decltype(std::declval<T>().Field, void(), \
- std::true_type()) \
- { \
- return {}; \
- } \
- template <typename T> \
- constexpr auto HasFieldImpl##Field(...) \
- { \
- return std::false_type{}; \
- } \
- template <typename T> \
- constexpr auto HasField##Field() \
- { \
- return HasFieldImpl##Field<T>(0); \
- }
-
-DECLARE_HAS_FIELD(FileName)
-DECLARE_HAS_FIELD(NextEntryOffset)
-DECLARE_HAS_FIELD(ShortName)
-
-#undef DECLARE_HAS_FIELD
-
-template <FILE_INFORMATION_CLASS fileInformationClass, class FileInformationClass>
-struct FileInformationClassUtilsImpl
-{
- static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName)
- {
- const FileInformationClass* info =
- reinterpret_cast<const FileInformationClass*>(address);
-
- // default value
- offset = sizeof(FileInformationClass);
-
- if constexpr (HasFieldFileName<FileInformationClass>()) {
- if constexpr (HasFieldNextEntryOffset<FileInformationClass>()) {
- offset = info->NextEntryOffset;
- }
- fileName = std::wstring(info->FileName, info->FileNameLength / sizeof(WCHAR));
- }
- }
-
- static void set_offset(LPVOID address, ULONG offset)
- {
- FileInformationClass* info = reinterpret_cast<FileInformationClass*>(address);
-
- if constexpr (HasFieldNextEntryOffset<FileInformationClass>()) {
- info->NextEntryOffset = offset;
- }
- }
-
- static void set_filename(LPVOID address, const std::wstring& fileName)
- {
- FileInformationClass* info = reinterpret_cast<FileInformationClass*>(address);
-
- if constexpr (HasFieldFileName<FileInformationClass>()) {
- memset(info->FileName, L'\0', info->FileNameLength);
- info->FileNameLength = static_cast<ULONG>(fileName.length() * sizeof(WCHAR));
-
- // doesn't need to be 0-terminated
- memcpy(info->FileName, fileName.c_str(), info->FileNameLength);
-
- if constexpr (HasFieldShortName<FileInformationClass>()) {
- if (info->ShortNameLength > 0) {
- info->ShortNameLength = static_cast<CCHAR>(
- GetShortPathNameW(fileName.c_str(), info->ShortName, 8));
- }
- }
- }
- }
-};
-
-template <FILE_INFORMATION_CLASS fileInformationClass>
-struct FileInformationClassUtils;
-
-template <>
-struct FileInformationClassUtils<FileDirectoryInformation>
- : FileInformationClassUtilsImpl<FileDirectoryInformation,
- FILE_DIRECTORY_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileFullDirectoryInformation>
- : FileInformationClassUtilsImpl<FileFullDirectoryInformation,
- FILE_FULL_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileBothDirectoryInformation>
- : FileInformationClassUtilsImpl<FileBothDirectoryInformation,
- FILE_BOTH_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileStandardInformation>
- : FileInformationClassUtilsImpl<FileStandardInformation, FILE_STANDARD_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileNameInformation>
- : FileInformationClassUtilsImpl<FileNameInformation, FILE_NAME_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileRenameInformation>
- : FileInformationClassUtilsImpl<FileRenameInformation, FILE_RENAME_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileNamesInformation>
- : FileInformationClassUtilsImpl<FileNamesInformation, FILE_NAMES_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileObjectIdInformation>
- : FileInformationClassUtilsImpl<FileObjectIdInformation, FILE_OBJECTID_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileReparsePointInformation>
- : FileInformationClassUtilsImpl<FileReparsePointInformation,
- FILE_REPARSE_POINT_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdBothDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdBothDirectoryInformation,
- FILE_ID_BOTH_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdFullDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdFullDirectoryInformation,
- FILE_ID_FULL_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileNormalizedNameInformation>
- : FileInformationClassUtilsImpl<FileNormalizedNameInformation,
- FILE_NAME_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdExtdDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdExtdDirectoryInformation,
- FILE_ID_EXTD_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdExtdBothDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdExtdBothDirectoryInformation,
- FILE_ID_EXTD_BOTH_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileId64ExtdDirectoryInformation>
- : FileInformationClassUtilsImpl<FileId64ExtdDirectoryInformation,
- FILE_ID_64_EXTD_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileId64ExtdBothDirectoryInformation>
- : FileInformationClassUtilsImpl<FileId64ExtdBothDirectoryInformation,
- FILE_ID_64_EXTD_BOTH_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdAllExtdDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdAllExtdDirectoryInformation,
- FILE_ID_ALL_EXTD_DIR_INFORMATION>
-{};
-template <>
-struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
- : FileInformationClassUtilsImpl<FileIdAllExtdBothDirectoryInformation,
- FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION>
-{};
-
-// FILE_ALL_INFORMATION needs to be handled differently because it has a field that
-// is itself a structure
-template <>
-struct FileInformationClassUtils<FileAllInformation>
-{
- static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName)
- {
- FileInformationClassUtils<FileNameInformation>::get_data(
- &reinterpret_cast<const FILE_ALL_INFORMATION*>(address)->NameInformation,
- offset, fileName);
- }
-
- static void set_offset(LPVOID address, ULONG offset)
- {
- // this is a no-op but it's consistent to do that everywhere
- FileInformationClassUtils<FileNameInformation>::set_offset(
- &reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, offset);
- }
-
- static void set_filename(LPVOID address, const std::wstring& fileName)
- {
- FileInformationClassUtils<FileNameInformation>::set_filename(
- &reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, fileName);
- }
-};
-
-} // namespace usvfs::details
-
-#define _APP_FINFO_CASE(clazz, fn, ...) \
- case clazz: \
- return usvfs::details::FileInformationClassUtils<clazz>::fn(__VA_ARGS__);
-
-#define _APPLY_FILEINFO_FN(fn, ...) \
- _APP_FINFO_CASE(FileAllInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileFullDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileBothDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileStandardInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileNameInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileRenameInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileNamesInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileObjectIdInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileReparsePointInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdBothDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdFullDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileNormalizedNameInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdExtdDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdExtdBothDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileId64ExtdDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileId64ExtdBothDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdAllExtdDirectoryInformation, fn, __VA_ARGS__) \
- _APP_FINFO_CASE(FileIdAllExtdBothDirectoryInformation, fn, __VA_ARGS__)
-
-void GetFileInformationData(FILE_INFORMATION_CLASS fileInformationClass,
- LPCVOID address, ULONG& offset, std::wstring& fileName)
-{
- switch (fileInformationClass) {
- _APPLY_FILEINFO_FN(get_data, address, offset, fileName);
- default:
- offset = ULONG_MAX;
- }
-}
-
-void SetFileInformationOffset(FILE_INFORMATION_CLASS fileInformationClass,
- LPVOID address, ULONG offset)
-{
- switch (fileInformationClass) {
- _APPLY_FILEINFO_FN(set_offset, address, offset);
- default:
- break;
- }
-}
-
-void SetFileInformationFileName(FILE_INFORMATION_CLASS fileInformationClass,
- LPVOID address, const std::wstring& fileName)
-{
- switch (fileInformationClass) {
- _APPLY_FILEINFO_FN(set_filename, address, fileName);
- default:
- break;
- }
-}
-
-#undef _APP_FINFO_CASE
-#undef _APPLY_FILEINFO_FN
diff --git a/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp b/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp
deleted file mode 100644
index 6599eb0..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/kernel32.cpp
+++ /dev/null
@@ -1,1860 +0,0 @@
-#include "kernel32.h"
-#include "sharedids.h"
-
-#include "../hookcallcontext.h"
-#include "../hookcontext.h"
-#include "../hookmanager.h"
-#include "../maptracker.h"
-#include <formatters.h>
-#include <inject.h>
-#include <loghelpers.h>
-#include <stringcast.h>
-#include <stringutils.h>
-#include <usvfs.h>
-#include <winapi.h>
-#include <winbase.h>
-
-namespace ush = usvfs::shared;
-using ush::CodePage;
-using ush::string_cast;
-
-namespace usvfs
-{
-MapTracker k32DeleteTracker;
-MapTracker k32FakeDirTracker;
-} // namespace usvfs
-
-class CurrentDirectoryTracker
-{
-public:
- using wstring = std::wstring;
-
- bool get(wstring& currentDir, const wchar_t* forRelativePath = nullptr)
- {
- int index = m_currentDrive;
- if (forRelativePath && *forRelativePath && forRelativePath[1] == ':')
- if (!getDriveIndex(forRelativePath, index))
- spdlog::get("usvfs")->warn(
- "CurrentDirectoryTracker::get() invalid drive letter: {}, will use current "
- "drive {}",
- string_cast<std::string>(forRelativePath),
- static_cast<char>('A' + index)); // prints '@' for m_currentDrive == -1
- if (index < 0)
- return false;
-
- std::shared_lock<std::shared_mutex> lock(m_mutex);
- if (m_perDrive[index].empty())
- return false;
- else {
- currentDir = m_perDrive[index];
- return true;
- }
- }
-
- bool set(const wstring& currentDir)
- {
- int index = -1;
- bool good = !currentDir.empty() && getDriveIndex(currentDir.c_str(), index) &&
- currentDir[1] == ':';
- std::unique_lock<std::shared_mutex> lock(m_mutex);
- m_currentDrive = good ? index : -1;
- if (good)
- m_perDrive[index] = currentDir;
- return good;
- }
-
-private:
- static bool getDriveIndex(const wchar_t* path, int& index)
- {
- if (*path >= 'a' && *path <= 'z')
- index = *path - 'a';
- else if (*path >= 'A' && *path <= 'Z')
- index = *path - 'A';
- else
- return false;
- return true;
- }
-
- mutable std::shared_mutex m_mutex;
- wstring m_perDrive['z' - 'a' + 1];
- int m_currentDrive{-1};
-};
-
-CurrentDirectoryTracker k32CurrentDirectoryTracker;
-
-// attempts to copy source to destination and return the error code
-static inline DWORD copyFileDirect(LPCWSTR source, LPCWSTR destination, bool overwrite)
-{
- usvfs::FunctionGroupLock lock(usvfs::MutExHookGroup::SHELL_FILEOP);
- return CopyFileExW(source, destination, NULL, NULL, NULL,
- overwrite ? 0 : COPY_FILE_FAIL_IF_EXISTS)
- ? ERROR_SUCCESS
- : GetLastError();
-}
-
-static inline WCHAR pathNameDriveLetter(LPCWSTR path)
-{
- if (!path || !path[0])
- return 0;
- if (path[1] == ':')
- return path[0];
- // if path is not ?: or \* then we need to get absolute path:
- std::wstring buf;
- if (path[0] != '\\') {
- buf = winapi::wide::getFullPathName(path).first;
- path = buf.c_str();
- if (!path[0] || path[1] == ':')
- return path[0];
- }
- // check for \??\C:
- if (path[1] && path[2] && path[3] && path[4] && path[0] == '\\' && path[3] == '\\' &&
- path[5] == ':')
- return path[4];
- // give up
- return 0;
-}
-
-// returns false also in case we fail to determine the drive letter of the path
-static inline bool pathsOnDifferentDrives(LPCWSTR path1, LPCWSTR path2)
-{
- WCHAR drive1 = pathNameDriveLetter(path1);
- WCHAR drive2 = pathNameDriveLetter(path2);
- return drive1 && drive2 && towupper(drive1) != towupper(drive2);
-}
-
-HMODULE WINAPI usvfs::hook_LoadLibraryExA(LPCSTR lpFileName, HANDLE hFile,
- DWORD dwFlags)
-{
- HMODULE res = nullptr;
-
- HOOK_START_GROUP(MutExHookGroup::LOAD_LIBRARY)
- const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName);
-
- PRE_REALCALL
- res = LoadLibraryExW(fileName.c_str(), hFile, dwFlags);
- POST_REALCALL
-
- HOOK_END
- return res;
-}
-
-HMODULE WINAPI usvfs::hook_LoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile,
- DWORD dwFlags)
-{
- HMODULE res = nullptr;
-
- HOOK_START_GROUP(MutExHookGroup::LOAD_LIBRARY)
- // Why is the usual if (!callContext.active()... check missing?
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName);
- PRE_REALCALL
- res = ::LoadLibraryExW(reroute.fileName(), hFile, dwFlags);
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAM(res)
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-/// determine name of the binary to run based on parameters for createprocess
-std::wstring getBinaryName(LPCWSTR applicationName, LPCWSTR lpCommandLine)
-{
- if (applicationName != nullptr) {
- std::pair<std::wstring, std::wstring> fullPath =
- winapi::wide::getFullPathName(applicationName);
- return fullPath.second;
- } else {
- if (lpCommandLine[0] == '"') {
- const wchar_t* endQuote = wcschr(lpCommandLine, '"');
- if (endQuote != nullptr) {
- return std::wstring(lpCommandLine + 1, endQuote - 1);
- }
- }
-
- // according to the documentation, if the commandline is unquoted and has
- // spaces, it will be interpreted in multiple ways, i.e.
- // c:\program.exe files\sub dir\program name
- // c:\program files\sub.exe dir\program name
- // c:\program files\sub dir\program.exe name
- // c:\program files\sub dir\program name.exe
- LPCWSTR space = wcschr(lpCommandLine, L' ');
- while (space != nullptr) {
- std::wstring subString(lpCommandLine, space);
- bool isDirectory = true;
- if (winapi::ex::wide::fileExists(subString.c_str(), &isDirectory) &&
- !isDirectory) {
- return subString;
- } else {
- space = wcschr(space + 1, L' ');
- }
- }
- return std::wstring(lpCommandLine);
- }
-}
-
-BOOL(WINAPI* usvfs::CreateProcessInternalW)(
- LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
- LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken);
-
-BOOL WINAPI usvfs::hook_CreateProcessInternalW(
- LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
- LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::CREATE_PROCESS)
- if (!callContext.active()) {
- res = CreateProcessInternalW(
- token, lpApplicationName, lpCommandLine, lpProcessAttributes,
- lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
- lpCurrentDirectory, lpStartupInfo, lpProcessInformation, newToken);
- callContext.updateLastError();
- return res;
- }
-
- // remember if the caller wanted the process to be suspended. If so, we
- // don't resume when we're done
- BOOL susp = dwCreationFlags & CREATE_SUSPENDED;
- dwCreationFlags |= CREATE_SUSPENDED;
-
- RerouteW applicationReroute;
- RerouteW cmdReroute;
- LPWSTR cend = nullptr;
-
- std::wstring dllPath;
- usvfsParameters callParameters;
-
- { // scope for context lock
- auto context = READ_CONTEXT();
-
- if (RerouteW::interestingPath(lpCommandLine)) {
- // First "argument" in the commandline is the command, we need to identify it and
- // reroute it:
- if (*lpCommandLine == '"') {
- // If the first argument is quoted we trust its is quoted correctly
- for (cend = lpCommandLine; *cend && *cend != ' '; ++cend)
- if (*cend == '"') {
- int escaped = 0;
- for (++cend; *cend && (*cend != '"' || escaped % 2 != 0); ++cend)
- escaped = *cend == '\\' ? escaped + 1 : 0;
- }
-
- if (*(cend - 1) == '"')
- --cend;
- auto old_cend = *cend;
- *cend = 0;
- cmdReroute = RerouteW::create(context, callContext, lpCommandLine + 1);
- *cend = old_cend;
- if (old_cend == '"')
- ++cend;
- } else {
- // If the first argument we have no choice but to test all the options to quote
- // the command as the real CreateProcess will do this:
- cend = lpCommandLine;
- while (true) {
- while (*cend && *cend != ' ')
- ++cend;
-
- auto old_cend = *cend;
- *cend = 0;
- cmdReroute = RerouteW::create(context, callContext, lpCommandLine);
- *cend = old_cend;
- if (cmdReroute.wasRerouted() || pathIsFile(cmdReroute.fileName()))
- break;
-
- while (*cend == ' ')
- ++cend;
-
- if (!*cend) {
- // if we reached the end of the string we'll just use the whole commandline
- // as is:
- cend = nullptr;
- break;
- }
- }
- }
- }
-
- applicationReroute = RerouteW::create(context, callContext, lpApplicationName);
-
- dllPath = context->dllPath();
- callParameters = context->callParameters();
- }
-
- std::wstring cmdline;
- if (cend && cmdReroute.fileName()) {
- auto fileName = cmdReroute.fileName();
- cmdline.reserve(wcslen(fileName) + wcslen(cend) + 2);
- if (*fileName != '"')
- cmdline += L"\"";
- cmdline += fileName;
- if (*fileName != '"')
- cmdline += L"\"";
- cmdline += cend;
- }
-
- PRE_REALCALL
- res = CreateProcessInternalW(token, applicationReroute.fileName(),
- cmdline.empty() ? lpCommandLine : &cmdline[0],
- lpProcessAttributes, lpThreadAttributes, bInheritHandles,
- dwCreationFlags, lpEnvironment, lpCurrentDirectory,
- lpStartupInfo, lpProcessInformation, newToken);
- POST_REALCALL
-
- BOOL blacklisted = FALSE;
- { // limit scope of context
- auto context = READ_CONTEXT();
- blacklisted = context->executableBlacklisted(applicationReroute.fileName(),
- cmdReroute.fileName());
- }
-
- if (res) {
- if (!blacklisted) {
- try {
- injectProcess(dllPath, callParameters, *lpProcessInformation);
- } catch (const std::exception& e) {
- spdlog::get("hooks")->error("failed to inject into {0}: {1}",
- lpApplicationName != nullptr
- ? applicationReroute.fileName()
- : static_cast<LPCWSTR>(lpCommandLine),
- e.what());
- }
- }
-
- // resume unless process is supposed to start suspended
- if (!susp && (ResumeThread(lpProcessInformation->hThread) == (DWORD)-1)) {
- spdlog::get("hooks")->error("failed to inject into spawned process");
- res = FALSE;
- }
- }
-
- LOG_CALL()
- .PARAM(lpApplicationName)
- .PARAM(applicationReroute.fileName())
- .PARAM(cmdReroute.fileName())
- .PARAM(res)
- .PARAM(callContext.lastError())
- .PARAM(cmdline);
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_GetFileAttributesExA(LPCSTR lpFileName,
- GET_FILEEX_INFO_LEVELS fInfoLevelId,
- LPVOID lpFileInformation)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = GetFileAttributesExA(lpFileName, fInfoLevelId, lpFileInformation);
- callContext.updateLastError();
- return res;
- }
- HOOK_END
-
- HOOK_START
- const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName);
-
- PRE_REALCALL
- res = GetFileAttributesExW(fileName.c_str(), fInfoLevelId, lpFileInformation);
- POST_REALCALL
-
- HOOK_END
- return res;
-}
-
-BOOL WINAPI usvfs::hook_GetFileAttributesExW(LPCWSTR lpFileName,
- GET_FILEEX_INFO_LEVELS fInfoLevelId,
- LPVOID lpFileInformation)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation);
- callContext.updateLastError();
- return res;
- }
-
- fs::path canonicalFile = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName));
-
- RerouteW reroute =
- RerouteW::create(READ_CONTEXT(), callContext, canonicalFile.c_str());
-
- PRE_REALCALL
- res = ::GetFileAttributesExW(reroute.fileName(), fInfoLevelId, lpFileInformation);
- POST_REALCALL
-
- DWORD originalError = callContext.lastError();
- DWORD fixedError = originalError;
- // In case the target does not exist the error value varies to differentiate if the
- // parent folder exists (ERROR_FILE_NOT_FOUND) or not (ERROR_PATH_NOT_FOUND).
- // If the original target's parent folder doesn't actually exist it may exist in the
- // the virtualized sense, or if we rerouted the query the parent of the original path
- // might exist while the parent of the rerouted path might not:
- if (!res && fixedError == ERROR_PATH_NOT_FOUND) {
- // first query original file parent (if we rerouted it):
- fs::path originalParent = canonicalFile.parent_path();
- WIN32_FILE_ATTRIBUTE_DATA parentAttr;
- if (reroute.wasRerouted() &&
- ::GetFileAttributesExW(originalParent.c_str(), GetFileExInfoStandard,
- &parentAttr) &&
- (parentAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- fixedError = ERROR_FILE_NOT_FOUND;
- else {
- // now query the rerouted path for parent (which can be different from the parent
- // of the rerouted path)
- RerouteW rerouteParent =
- RerouteW::create(READ_CONTEXT(), callContext, originalParent.c_str());
- if (rerouteParent.wasRerouted() &&
- ::GetFileAttributesExW(rerouteParent.fileName(), GetFileExInfoStandard,
- &parentAttr) &&
- (parentAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- fixedError = ERROR_FILE_NOT_FOUND;
- }
- }
- if (fixedError != originalError)
- callContext.updateLastError(fixedError);
-
- if (reroute.wasRerouted() || fixedError != originalError) {
- DWORD resAttrib;
- if (res && fInfoLevelId == GetFileExInfoStandard && lpFileInformation)
- resAttrib = reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA*>(lpFileInformation)
- ->dwFileAttributes;
- else
- resAttrib = (DWORD)-1;
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(fInfoLevelId)
- .PARAMHEX(res)
- .PARAMHEX(resAttrib)
- .PARAM(originalError)
- .PARAM(fixedError);
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetFileAttributesA(LPCSTR lpFileName)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = GetFileAttributesA(lpFileName);
- callContext.updateLastError();
- return res;
- }
- HOOK_END
-
- HOOK_START
- const std::wstring fileName = ush::string_cast<std::wstring>(lpFileName);
-
- PRE_REALCALL
- res = GetFileAttributesW(fileName.c_str());
- POST_REALCALL
-
- HOOK_END
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetFileAttributesW(LPCWSTR lpFileName)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = GetFileAttributesW(lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- fs::path canonicalFile = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName));
-
- RerouteW reroute =
- RerouteW::create(READ_CONTEXT(), callContext, canonicalFile.c_str());
-
- if (reroute.wasRerouted())
- PRE_REALCALL
- res = ::GetFileAttributesW(reroute.fileName());
- POST_REALCALL
-
- DWORD originalError = callContext.lastError();
- DWORD fixedError = originalError;
- // In case the target does not exist the error value varies to differentiate if the
- // parent folder exists (ERROR_FILE_NOT_FOUND) or not (ERROR_PATH_NOT_FOUND).
- // If the original target's parent folder doesn't actually exist it may exist in the
- // the virtualized sense, or if we rerouted the query the parent of the original path
- // might exist while the parent of the rerouted path might not:
- if (res == INVALID_FILE_ATTRIBUTES && fixedError == ERROR_PATH_NOT_FOUND) {
- // first query original file parent (if we rerouted it):
- fs::path originalParent = canonicalFile.parent_path();
- DWORD attr;
- if (reroute.wasRerouted() &&
- (attr = ::GetFileAttributesW(originalParent.c_str())) !=
- INVALID_FILE_ATTRIBUTES &&
- (attr & FILE_ATTRIBUTE_DIRECTORY))
- fixedError = ERROR_FILE_NOT_FOUND;
- else {
- // now query the rerouted path for parent (which can be different from the parent
- // of the rerouted path)
- RerouteW rerouteParent =
- RerouteW::create(READ_CONTEXT(), callContext, originalParent.c_str());
- if (rerouteParent.wasRerouted() &&
- (attr = ::GetFileAttributesW(rerouteParent.fileName())) !=
- INVALID_FILE_ATTRIBUTES &&
- (attr & FILE_ATTRIBUTE_DIRECTORY))
- fixedError = ERROR_FILE_NOT_FOUND;
- }
- }
- if (fixedError != originalError)
- callContext.updateLastError(fixedError);
-
- if (reroute.wasRerouted() || fixedError != originalError) {
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(originalError)
- .PARAM(fixedError);
- }
-
- HOOK_ENDP(lpFileName);
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- // Why is the usual if (!callContext.active()... check missing?
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName);
- PRE_REALCALL
- res = ::SetFileAttributesW(reroute.fileName(), dwFileAttributes);
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL().PARAM(reroute.fileName()).PARAM(res).PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_DeleteFileW(LPCWSTR lpFileName)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::DELETE_FILE)
- // Why is the usual if (!callContext.active()... check missing?
-
- const std::wstring path =
- RerouteW::canonizePath(RerouteW::absolutePath(lpFileName)).wstring();
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, path.c_str());
-
- PRE_REALCALL
- if (reroute.wasRerouted()) {
- res = ::DeleteFileW(reroute.fileName());
- } else {
- res = ::DeleteFileW(path.c_str());
- }
- POST_REALCALL
-
- if (res) {
- reroute.removeMapping(READ_CONTEXT());
- }
-
- if (reroute.wasRerouted())
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-BOOL rewriteChangedDrives(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName,
- const usvfs::RerouteW& readReroute,
- const usvfs::CreateRerouter& writeReroute)
-{
- return ((readReroute.wasRerouted() || writeReroute.wasRerouted()) &&
- pathsOnDifferentDrives(readReroute.fileName(), writeReroute.fileName()) &&
- !pathsOnDifferentDrives(lpExistingFileName, lpNewFileName));
-}
-
-BOOL WINAPI usvfs::hook_MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
-
- if (!callContext.active()) {
- res = MoveFileA(lpExistingFileName, lpNewFileName);
- callContext.updateLastError();
- return res;
- }
-
- HOOK_END
- HOOK_START
-
- const auto& existingFileName = ush::string_cast<std::wstring>(lpExistingFileName);
- const auto& newFileName = ush::string_cast<std::wstring>(lpNewFileName);
-
- PRE_REALCALL
- res = MoveFileW(existingFileName.c_str(), newFileName.c_str());
- POST_REALCALL
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
- if (!callContext.active()) {
- res = MoveFileW(lpExistingFileName, lpNewFileName);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW readReroute;
- CreateRerouter writeReroute;
- bool callOriginal = true;
- DWORD newFlags = 0;
-
- {
- auto context = READ_CONTEXT();
- readReroute = RerouteW::create(context, callContext, lpExistingFileName);
- callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName, false,
- "hook_MoveFileW");
- }
-
- if (callOriginal) {
- bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName,
- readReroute, writeReroute);
- if (movedDrives)
- newFlags |= MOVEFILE_COPY_ALLOWED;
-
- bool isDirectory = pathIsDirectory(readReroute.fileName());
-
- PRE_REALCALL
- if (isDirectory && movedDrives) {
- SHFILEOPSTRUCTW sf = {0};
- sf.wFunc = FO_MOVE;
- sf.hwnd = 0;
- sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI;
- sf.pFrom = readReroute.fileName();
- sf.pTo = writeReroute.fileName();
- int shRes = ::SHFileOperationW(&sf);
- switch (shRes) {
- case 0x78:
- callContext.updateLastError(ERROR_ACCESS_DENIED);
- break;
- case 0x7C:
- callContext.updateLastError(ERROR_FILE_NOT_FOUND);
- break;
- case 0x7E:
- case 0x80:
- callContext.updateLastError(ERROR_FILE_EXISTS);
- break;
- default:
- callContext.updateLastError(shRes);
- }
- res = shRes == 0;
- } else if (newFlags)
- res = ::MoveFileExW(readReroute.fileName(), writeReroute.fileName(), newFlags);
- else
- res = ::MoveFileW(readReroute.fileName(), writeReroute.fileName());
- POST_REALCALL
-
- if (res)
- SetLastError(ERROR_SUCCESS);
-
- writeReroute.updateResult(callContext, res);
-
- if (res) {
- readReroute.removeMapping(
- READ_CONTEXT(), isDirectory); // Updating the rerouteCreate to check deleted
- // file entries should make this okay
-
- if (writeReroute.newReroute()) {
- if (isDirectory)
- RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName),
- fs::path(writeReroute.fileName()));
- else
- writeReroute.insertMapping(WRITE_CONTEXT());
- }
- }
-
- if (readReroute.wasRerouted() || writeReroute.wasRerouted() ||
- writeReroute.changedError())
- LOG_CALL()
- .PARAM(readReroute.fileName())
- .PARAM(writeReroute.fileName())
- .PARAMWRAP(newFlags)
- .PARAM(res)
- .PARAM(writeReroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName,
- DWORD dwFlags)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
-
- if (!callContext.active()) {
- res = MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags);
- callContext.updateLastError();
- return res;
- }
-
- HOOK_END
- HOOK_START
-
- const std::wstring existingFileName =
- ush::string_cast<std::wstring>(lpExistingFileName);
-
- // careful: lpNewFileName can be null if dwFlags is
- // MOVEFILE_DELAY_UNTIL_REBOOT, so don't blindly cast the string and make sure
- // the null pointer is forwarded correctly
- std::wstring newFileNameWstring;
- const wchar_t* newFileName = nullptr;
-
- if (lpNewFileName) {
- newFileNameWstring = ush::string_cast<std::wstring>(lpNewFileName);
- newFileName = newFileNameWstring.c_str();
- }
-
- PRE_REALCALL
- res = MoveFileExW(existingFileName.c_str(), newFileName, dwFlags);
- POST_REALCALL
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName,
- DWORD dwFlags)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
- if (!callContext.active()) {
- res = MoveFileExW(lpExistingFileName, lpNewFileName, dwFlags);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW readReroute;
- CreateRerouter writeReroute;
- bool callOriginal = true;
- DWORD newFlags = dwFlags;
-
- {
- auto context = READ_CONTEXT();
- readReroute = RerouteW::create(context, callContext, lpExistingFileName);
- callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName,
- newFlags & MOVEFILE_REPLACE_EXISTING,
- "hook_MoveFileExW");
- }
-
- if (callOriginal) {
- bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName,
- readReroute, writeReroute);
-
- bool isDirectory = pathIsDirectory(readReroute.fileName());
-
- PRE_REALCALL
- if (isDirectory && movedDrives) {
- SHFILEOPSTRUCTW sf = {0};
- sf.wFunc = FO_MOVE;
- sf.hwnd = 0;
- sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI;
- sf.pFrom = readReroute.fileName();
- sf.pTo = writeReroute.fileName();
- int shRes = ::SHFileOperationW(&sf);
- switch (shRes) {
- case 0x78:
- callContext.updateLastError(ERROR_ACCESS_DENIED);
- break;
- case 0x7C:
- callContext.updateLastError(ERROR_FILE_NOT_FOUND);
- break;
- case 0x7E:
- case 0x80:
- callContext.updateLastError(ERROR_FILE_EXISTS);
- break;
- default:
- callContext.updateLastError(shRes);
- }
- res = shRes == 0;
- } else
- res = ::MoveFileExW(readReroute.fileName(), writeReroute.fileName(), newFlags);
- POST_REALCALL
-
- if (res)
- SetLastError(ERROR_SUCCESS);
-
- writeReroute.updateResult(callContext, res);
-
- if (res) {
- readReroute.removeMapping(
- READ_CONTEXT(), isDirectory); // Updating the rerouteCreate to check deleted
- // file entries should make this okay
-
- if (writeReroute.newReroute()) {
- if (isDirectory)
- RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName),
- fs::path(writeReroute.fileName()));
- else
- writeReroute.insertMapping(WRITE_CONTEXT());
- }
- }
-
- if (readReroute.wasRerouted() || writeReroute.wasRerouted() ||
- writeReroute.changedError())
- LOG_CALL()
- .PARAM(readReroute.fileName())
- .PARAM(writeReroute.fileName())
- .PARAMWRAP(dwFlags)
- .PARAMWRAP(newFlags)
- .PARAM(res)
- .PARAM(writeReroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_MoveFileWithProgressA(LPCSTR lpExistingFileName,
- LPCSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData, DWORD dwFlags)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
-
- if (!callContext.active()) {
- res = MoveFileWithProgressA(lpExistingFileName, lpNewFileName, lpProgressRoutine,
- lpData, dwFlags);
- callContext.updateLastError();
- return res;
- }
-
- HOOK_END
- HOOK_START
-
- const auto& existingFileName = ush::string_cast<std::wstring>(lpExistingFileName);
-
- // careful: lpNewFileName can be null if dwFlags is
- // MOVEFILE_DELAY_UNTIL_REBOOT, so don't blindly cast the string and make sure
- // the null pointer is forwarded correctly
- std::wstring newFileNameWstring;
- const wchar_t* newFileName = nullptr;
-
- if (lpNewFileName) {
- newFileNameWstring = ush::string_cast<std::wstring>(lpNewFileName);
- newFileName = newFileNameWstring.c_str();
- }
-
- PRE_REALCALL
- res = MoveFileWithProgressW(existingFileName.c_str(), newFileName, lpProgressRoutine,
- lpData, dwFlags);
- POST_REALCALL
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_MoveFileWithProgressW(LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData, DWORD dwFlags)
-{
-
- // TODO: Remove all redundant hooks to moveFile alternatives.
- // it would appear that all other moveFile functions end up calling this one with no
- // additional code.
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
- if (!callContext.active()) {
- res = MoveFileWithProgressW(lpExistingFileName, lpNewFileName, lpProgressRoutine,
- lpData, dwFlags);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW readReroute;
- usvfs::CreateRerouter writeReroute;
- bool callOriginal = true;
- DWORD newFlags = dwFlags;
-
- {
- auto context = READ_CONTEXT();
- readReroute = RerouteW::create(context, callContext, lpExistingFileName);
- callOriginal = writeReroute.rerouteNew(context, callContext, lpNewFileName,
- newFlags & MOVEFILE_REPLACE_EXISTING,
- "hook_MoveFileWithProgressW");
- }
-
- if (callOriginal) {
- bool movedDrives = rewriteChangedDrives(lpExistingFileName, lpNewFileName,
- readReroute, writeReroute);
- if (movedDrives)
- newFlags |= MOVEFILE_COPY_ALLOWED;
-
- bool isDirectory = pathIsDirectory(readReroute.fileName());
-
- PRE_REALCALL
- if (isDirectory && movedDrives) {
- SHFILEOPSTRUCTW sf = {0};
- sf.wFunc = FO_MOVE;
- sf.hwnd = 0;
- sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI;
- sf.pFrom = readReroute.fileName();
- sf.pTo = writeReroute.fileName();
- int shRes = ::SHFileOperationW(&sf);
- switch (shRes) {
- case 0x78:
- callContext.updateLastError(ERROR_ACCESS_DENIED);
- break;
- case 0x7C:
- callContext.updateLastError(ERROR_FILE_NOT_FOUND);
- break;
- case 0x7E:
- case 0x80:
- callContext.updateLastError(ERROR_FILE_EXISTS);
- break;
- default:
- callContext.updateLastError(shRes);
- }
- res = shRes == 0;
- } else
- res = ::MoveFileWithProgressW(readReroute.fileName(), writeReroute.fileName(),
- lpProgressRoutine, lpData, newFlags);
- POST_REALCALL
-
- if (res)
- SetLastError(ERROR_SUCCESS);
-
- writeReroute.updateResult(callContext, res);
-
- if (res) {
- // TODO: this call causes the node to be removed twice in case of
- // MOVEFILE_COPY_ALLOWED as the deleteFile hook lower level already takes care of
- // it, but deleteFile can't be disabled since we are relying on it in case of
- // MOVEFILE_REPLACE_EXISTING for the destination file.
- readReroute.removeMapping(
- READ_CONTEXT(),
- isDirectory); // Updating the rerouteCreate to check deleted file entries
- // should make this okay (not related to comments above)
-
- if (writeReroute.newReroute()) {
- if (isDirectory)
- RerouteW::addDirectoryMapping(WRITE_CONTEXT(), fs::path(lpNewFileName),
- fs::path(writeReroute.fileName()));
- else
- writeReroute.insertMapping(WRITE_CONTEXT());
- }
- }
-
- if (readReroute.wasRerouted() || writeReroute.wasRerouted() ||
- writeReroute.changedError())
- LOG_CALL()
- .PARAM(readReroute.fileName())
- .PARAM(writeReroute.fileName())
- .PARAMWRAP(dwFlags)
- .PARAMWRAP(newFlags)
- .PARAM(res)
- .PARAM(writeReroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_CopyFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData,
- LPBOOL pbCancel, DWORD dwCopyFlags)
-{
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
- if (!callContext.active()) {
- res = CopyFileExW(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData,
- pbCancel, dwCopyFlags);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW readReroute;
- usvfs::CreateRerouter writeReroute;
- bool callOriginal = true;
-
- {
- auto context = READ_CONTEXT();
- readReroute = RerouteW::create(context, callContext, lpExistingFileName);
- callOriginal = writeReroute.rerouteNew(
- context, callContext, lpNewFileName,
- (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) == 0, "hook_CopyFileExW");
- }
-
- if (callOriginal) {
- PRE_REALCALL
- res = ::CopyFileExW(readReroute.fileName(), writeReroute.fileName(),
- lpProgressRoutine, lpData, pbCancel, dwCopyFlags);
- POST_REALCALL
- writeReroute.updateResult(callContext, res);
-
- if (res && writeReroute.newReroute())
- writeReroute.insertMapping(WRITE_CONTEXT());
-
- if (readReroute.wasRerouted() || writeReroute.wasRerouted() ||
- writeReroute.changedError())
- LOG_CALL()
- .PARAM(readReroute.fileName())
- .PARAM(writeReroute.fileName())
- .PARAM(res)
- .PARAM(writeReroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer)
-{
- DWORD res = 0;
-
- HOOK_START
-
- std::wstring buffer;
- buffer.resize(nBufferLength);
-
- PRE_REALCALL
- res = GetCurrentDirectoryW(nBufferLength, &buffer[0]);
- POST_REALCALL
-
- if (res > 0) {
- res = WideCharToMultiByte(CP_ACP, 0, buffer.c_str(), res + 1, lpBuffer,
- nBufferLength, nullptr, nullptr);
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer)
-{
- DWORD res = FALSE;
-
- HOOK_START
-
- std::wstring actualCWD;
-
- if (!k32CurrentDirectoryTracker.get(actualCWD)) {
- PRE_REALCALL
- res = ::GetCurrentDirectoryW(nBufferLength, lpBuffer);
- POST_REALCALL
- } else {
- ush::wcsncpy_sz(lpBuffer, &actualCWD[0],
- std::min(static_cast<size_t>(nBufferLength), actualCWD.size() + 1));
-
- // yupp, that's how GetCurrentDirectory actually works...
- if (actualCWD.size() < nBufferLength) {
- res = static_cast<DWORD>(actualCWD.size());
- } else {
- res = static_cast<DWORD>(actualCWD.size() + 1);
- }
- }
-
- if (nBufferLength)
- LOG_CALL()
- .PARAM(std::wstring(lpBuffer, res))
- .PARAM(nBufferLength)
- .PARAM(actualCWD.size())
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_SetCurrentDirectoryA(LPCSTR lpPathName)
-{
- return SetCurrentDirectoryW(ush::string_cast<std::wstring>(lpPathName).c_str());
-}
-
-BOOL WINAPI usvfs::hook_SetCurrentDirectoryW(LPCWSTR lpPathName)
-{
- BOOL res = FALSE;
-
- HOOK_START
-
- const fs::path& realPath = RerouteW::canonizePath(RerouteW::absolutePath(lpPathName));
- const std::wstring& realPathStr = realPath.wstring();
- std::wstring finalRoute;
- BOOL found = FALSE;
-
- if (fs::exists(realPath))
- finalRoute = realPathStr;
- else {
- WCHAR processDir[MAX_PATH];
- if (::GetModuleFileNameW(NULL, processDir, MAX_PATH) != 0 &&
- ::PathRemoveFileSpecW(processDir)) {
- WCHAR processName[MAX_PATH];
- ::GetModuleFileNameW(NULL, processName, MAX_PATH);
- fs::path routedName = realPath / processName;
- RerouteW rerouteTest =
- RerouteW::create(READ_CONTEXT(), callContext, routedName.wstring().c_str());
- if (rerouteTest.wasRerouted()) {
- std::wstring reroutedPath = rerouteTest.fileName();
- if (routedName.wstring().find(processDir) != std::string::npos) {
- fs::path finalPath(reroutedPath);
- finalRoute = finalPath.parent_path().wstring();
- found = TRUE;
- }
- }
- }
-
- if (!found) {
- RerouteW reroute =
- RerouteW::create(READ_CONTEXT(), callContext, realPathStr.c_str());
- finalRoute = reroute.fileName();
- }
- }
-
- PRE_REALCALL
- res = ::SetCurrentDirectoryW(finalRoute.c_str());
- POST_REALCALL
-
- if (res)
- if (!k32CurrentDirectoryTracker.set(realPathStr))
- spdlog::get("usvfs")->warn("Updating actual current directory failed: {} ?!",
- string_cast<std::string>(realPathStr));
-
- LOG_CALL()
- .PARAM(lpPathName)
- .PARAM(realPathStr)
- .PARAM(finalRoute)
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-DLLEXPORT BOOL WINAPI usvfs::hook_CreateDirectoryW(
- LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
-{
- BOOL res = FALSE;
- HOOK_START
-
- RerouteW reroute = RerouteW::createOrNew(READ_CONTEXT(), callContext, lpPathName);
-
- PRE_REALCALL
- res = ::CreateDirectoryW(reroute.fileName(), lpSecurityAttributes);
- POST_REALCALL
-
- if (res && reroute.newReroute())
- reroute.insertMapping(WRITE_CONTEXT(), true);
-
- if (reroute.wasRerouted())
- LOG_CALL()
- .PARAM(lpPathName)
- .PARAM(reroute.fileName())
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-DLLEXPORT BOOL WINAPI usvfs::hook_RemoveDirectoryW(LPCWSTR lpPathName)
-{
-
- BOOL res = FALSE;
-
- HOOK_START_GROUP(MutExHookGroup::DELETE_FILE)
- // Why is the usual if (!callContext.active()... check missing?
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpPathName);
-
- PRE_REALCALL
- if (reroute.wasRerouted()) {
- res = ::RemoveDirectoryW(reroute.fileName());
- } else {
- res = ::RemoveDirectoryW(lpPathName);
- }
- POST_REALCALL
-
- if (res) {
- reroute.removeMapping(READ_CONTEXT(), true);
- }
-
- if (reroute.wasRerouted())
- LOG_CALL()
- .PARAM(lpPathName)
- .PARAM(reroute.fileName())
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength,
- LPSTR lpBuffer, LPSTR* lpFilePart)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::FULL_PATHNAME)
- if (!callContext.active()) {
- res = GetFullPathNameA(lpFileName, nBufferLength, lpBuffer, lpFilePart);
- callContext.updateLastError();
- return res;
- }
-
- std::string resolvedWithCMD;
-
- std::wstring actualCWD;
- fs::path filePath = ush::string_cast<std::wstring>(lpFileName, CodePage::UTF8);
- if (k32CurrentDirectoryTracker.get(actualCWD, filePath.wstring().c_str())) {
- if (!filePath.is_absolute())
- resolvedWithCMD = ush::string_cast<std::string>(
- (actualCWD / filePath.relative_path()).wstring());
- }
-
- PRE_REALCALL
- res =
- ::GetFullPathNameA(resolvedWithCMD.empty() ? lpFileName : resolvedWithCMD.c_str(),
- nBufferLength, lpBuffer, lpFilePart);
- POST_REALCALL
-
- if (false && nBufferLength)
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(resolvedWithCMD)
- .PARAM(std::string(lpBuffer, res))
- .PARAM(nBufferLength)
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength,
- LPWSTR lpBuffer, LPWSTR* lpFilePart)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::FULL_PATHNAME)
- if (!callContext.active()) {
- res = GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
- callContext.updateLastError();
- return res;
- }
-
- std::wstring resolvedWithCMD;
-
- std::wstring actualCWD;
- if (k32CurrentDirectoryTracker.get(actualCWD, lpFileName)) {
- fs::path filePath = lpFileName;
- if (!filePath.is_absolute())
- resolvedWithCMD = (actualCWD / filePath.relative_path()).wstring();
- }
-
- PRE_REALCALL
- res =
- ::GetFullPathNameW(resolvedWithCMD.empty() ? lpFileName : resolvedWithCMD.c_str(),
- nBufferLength, lpBuffer, lpFilePart);
- POST_REALCALL
-
- if (false && nBufferLength)
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(resolvedWithCMD)
- .PARAM(std::wstring(lpBuffer, res))
- .PARAM(nBufferLength)
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename,
- DWORD nSize)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS)
-
- PRE_REALCALL
- res = ::GetModuleFileNameA(hModule, lpFilename, nSize);
- POST_REALCALL
- if ((res != 0) && callContext.active()) {
- std::vector<char> buf;
- // If GetModuleFileNameA failed because the buffer is not large enough this
- // complicates matters because we are dealing with incomplete information
- // (consider for example the case that we have a long real path which will
- // be routed to a short virtual so the call should actually succeed in such
- // a case). To solve this we simply use our own buffer to find the complete
- // module path:
- DWORD full_res = res;
- size_t buf_size = nSize;
- while (full_res == buf_size) {
- buf_size = std::max(static_cast<size_t>(MAX_PATH), buf_size * 2);
- buf.resize(buf_size);
- full_res = ::GetModuleFileNameA(hModule, buf.data(), buf_size);
- }
-
- RerouteW reroute = RerouteW::create(
- READ_CONTEXT(), callContext,
- ush::string_cast<std::wstring>(buf.empty() ? lpFilename : buf.data()).c_str(),
- true);
- if (reroute.wasRerouted()) {
- DWORD reroutedSize = static_cast<DWORD>(wcslen(reroute.fileName()));
- if (reroutedSize >= nSize) {
- reroutedSize = nSize - 1;
- callContext.updateLastError(ERROR_INSUFFICIENT_BUFFER);
- res = nSize;
- } else
- res = reroutedSize;
- memcpy(lpFilename, ush::string_cast<std::string>(reroute.fileName()).c_str(),
- reroutedSize * sizeof(lpFilename[0]));
- lpFilename[reroutedSize] = 0;
-
- LOG_CALL()
- .PARAM(hModule)
- .addParam("lpFilename", (res != 0UL) ? lpFilename : "<not set>")
- .PARAM(nSize)
- .PARAM(res)
- .PARAM(callContext.lastError());
- }
- }
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename,
- DWORD nSize)
-{
- DWORD res = 0UL;
-
- HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS)
-
- PRE_REALCALL
- res = ::GetModuleFileNameW(hModule, lpFilename, nSize);
- POST_REALCALL
- if ((res != 0) && callContext.active()) {
- std::vector<WCHAR> buf;
- // If GetModuleFileNameW failed because the buffer is not large enough this
- // complicates matters because we are dealing with incomplete information (consider
- // for example the case that we have a long real path which will be routed to a
- // short virtual so the call should actually succeed in such a case). To solve this
- // we simply use our own buffer to find the complete module path:
- DWORD full_res = res;
- size_t buf_size = nSize;
- while (full_res == buf_size) {
- buf_size = std::max(static_cast<size_t>(MAX_PATH), buf_size * 2);
- buf.resize(buf_size);
- full_res = ::GetModuleFileNameW(hModule, buf.data(), buf_size);
- }
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext,
- buf.empty() ? lpFilename : buf.data(), true);
- if (reroute.wasRerouted()) {
- DWORD reroutedSize = static_cast<DWORD>(wcslen(reroute.fileName()));
- if (reroutedSize >= nSize) {
- reroutedSize = nSize - 1;
- callContext.updateLastError(ERROR_INSUFFICIENT_BUFFER);
- res = nSize;
- } else
- res = reroutedSize;
- memcpy(lpFilename, reroute.fileName(), reroutedSize * sizeof(lpFilename[0]));
- lpFilename[reroutedSize] = 0;
-
- LOG_CALL()
- .PARAM(hModule)
- .addParam("lpFilename", (res != 0UL) ? lpFilename : L"<not set>")
- .PARAM(nSize)
- .PARAM(res)
- .PARAM(callContext.lastError());
- }
- }
- HOOK_END
-
- return res;
-}
-
-HANDLE WINAPI usvfs::hook_FindFirstFileExW(
- LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
- FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
-{
- HANDLE res = INVALID_HANDLE_VALUE;
-
- HOOK_START_GROUP(MutExHookGroup::SEARCH_FILES)
- if (!callContext.active()) {
- res = FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp,
- lpSearchFilter, dwAdditionalFlags);
- callContext.updateLastError();
- return res;
- }
-
- // FindFirstFileEx() must fail early if the path ends with a slash
- if (lpFileName) {
- const auto len = wcslen(lpFileName);
- if (len > 0) {
- if (lpFileName[len - 1] == L'\\' || lpFileName[len - 1] == L'/') {
- spdlog::get("usvfs")->warn(
- "hook_FindFirstFileExW(): path '{}' ends with slash, always fails",
- fs::path(lpFileName).string());
- return INVALID_HANDLE_VALUE;
- }
- }
- }
-
- fs::path finalPath;
- RerouteW reroute;
- fs::path originalPath;
-
- bool usedRewrite = false;
-
- // We need to do some trickery here, since we only want to use the hooked
- // NtQueryDirectoryFile for rerouted locations we need to check if the Directory path
- // has been routed instead of the full path.
- originalPath = RerouteW::canonizePath(RerouteW::absolutePath(lpFileName));
- PRE_REALCALL
- res = ::FindFirstFileExW(originalPath.c_str(), fInfoLevelId, lpFindFileData,
- fSearchOp, lpSearchFilter, dwAdditionalFlags);
- POST_REALCALL
-
- if (res == INVALID_HANDLE_VALUE) {
- fs::path searchPath = originalPath.filename();
- fs::path parentPath = originalPath.parent_path();
- std::wstring findPath = parentPath.wstring();
- while (findPath.find(L"*?<>\"", 0, 1) != std::wstring::npos) {
- searchPath = parentPath.filename() / searchPath;
- parentPath = parentPath.parent_path();
- findPath = parentPath.wstring();
- }
- reroute = RerouteW::create(READ_CONTEXT(), callContext, parentPath.c_str());
- if (reroute.wasRerouted()) {
- finalPath = reroute.fileName();
- finalPath /= searchPath.wstring();
- }
- if (!finalPath.empty()) {
- PRE_REALCALL
- usedRewrite = true;
- res = ::FindFirstFileExW(finalPath.c_str(), fInfoLevelId, lpFindFileData,
- fSearchOp, lpSearchFilter, dwAdditionalFlags);
- POST_REALCALL
- }
- }
-
- if (res != INVALID_HANDLE_VALUE) {
- // store the original search path for use during iteration
- WRITE_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[res] = lpFileName;
- }
-
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(originalPath.c_str())
- .PARAM(res)
- .PARAM(callContext.lastError());
- if (usedRewrite)
- LOG_CALL()
- .PARAM(lpFileName)
- .PARAM(finalPath.c_str())
- .PARAM(res)
- .PARAM(callContext.lastError());
-
- HOOK_END
-
- return res;
-}
-
-HRESULT(WINAPI* usvfs::CopyFile2)(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName,
- COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters);
-
-HRESULT WINAPI usvfs::hook_CopyFile2(PCWSTR pwszExistingFileName,
- PCWSTR pwszNewFileName,
- COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters)
-{
- HRESULT res = E_FAIL;
-
- typedef HRESULT(WINAPI * CopyFile2_t)(PCWSTR, PCWSTR, COPYFILE2_EXTENDED_PARAMETERS*);
-
- HOOK_START_GROUP(MutExHookGroup::SHELL_FILEOP)
- if (!callContext.active()) {
- res = CopyFile2(pwszExistingFileName, pwszNewFileName, pExtendedParameters);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW readReroute;
- CreateRerouter writeReroute;
- bool callOriginal = true;
-
- {
- auto context = READ_CONTEXT();
- readReroute = RerouteW::create(context, callContext, pwszExistingFileName);
- callOriginal = writeReroute.rerouteNew(
- context, callContext, pwszNewFileName,
- pExtendedParameters &&
- (pExtendedParameters->dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) == 0,
- "hook_CopyFile2");
- }
-
- if (callOriginal) {
- PRE_REALCALL
- res =
- CopyFile2(readReroute.fileName(), writeReroute.fileName(), pExtendedParameters);
- POST_REALCALL
- writeReroute.updateResult(callContext, SUCCEEDED(res));
-
- if (SUCCEEDED(res) && writeReroute.newReroute())
- writeReroute.insertMapping(WRITE_CONTEXT());
-
- if (readReroute.wasRerouted() || writeReroute.wasRerouted() ||
- writeReroute.changedError())
- LOG_CALL()
- .PARAM(readReroute.fileName())
- .PARAM(writeReroute.fileName())
- .PARAM(res)
- .PARAM(writeReroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetPrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName,
- LPCSTR lpDefault,
- LPSTR lpReturnedString, DWORD nSize,
- LPCSTR lpFileName)
-{
- DWORD res = 0;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::GetPrivateProfileStringA(lpAppName, lpKeyName, lpDefault, lpReturnedString,
- nSize, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW reroute = RerouteW::create(
- READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str());
-
- PRE_REALCALL
- res = ::GetPrivateProfileStringA(
- lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize,
- ush::string_cast<std::string>(reroute.fileName()).c_str());
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpKeyName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetPrivateProfileStringW(LPCWSTR lpAppName, LPCWSTR lpKeyName,
- LPCWSTR lpDefault,
- LPWSTR lpReturnedString, DWORD nSize,
- LPCWSTR lpFileName)
-{
- DWORD res = 0;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString,
- nSize, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName);
-
- PRE_REALCALL
- res = ::GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString,
- nSize, reroute.fileName());
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpKeyName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetPrivateProfileSectionA(LPCSTR lpAppName,
- LPSTR lpReturnedString, DWORD nSize,
- LPCSTR lpFileName)
-{
- DWORD res = 0;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::GetPrivateProfileSectionA(lpAppName, lpReturnedString, nSize, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW reroute = RerouteW::create(
- READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str());
-
- PRE_REALCALL
- res = ::GetPrivateProfileSectionA(
- lpAppName, lpReturnedString, nSize,
- ush::string_cast<std::string>(reroute.fileName()).c_str());
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-DWORD WINAPI usvfs::hook_GetPrivateProfileSectionW(LPCWSTR lpAppName,
- LPWSTR lpReturnedString, DWORD nSize,
- LPCWSTR lpFileName)
-{
- DWORD res = 0;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::GetPrivateProfileSectionW(lpAppName, lpReturnedString, nSize, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- RerouteW reroute = RerouteW::create(READ_CONTEXT(), callContext, lpFileName);
-
- PRE_REALCALL
- res = ::GetPrivateProfileSectionW(lpAppName, lpReturnedString, nSize,
- reroute.fileName());
- POST_REALCALL
-
- if (reroute.wasRerouted()) {
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_WritePrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName,
- LPCSTR lpString, LPCSTR lpFileName)
-{
- BOOL res = false;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::WritePrivateProfileStringA(lpAppName, lpKeyName, lpString, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- CreateRerouter reroute;
- bool callOriginal = reroute.rerouteNew(
- READ_CONTEXT(), callContext, ush::string_cast<std::wstring>(lpFileName).c_str(),
- true, "hook_WritePrivateProfileStringA");
-
- if (callOriginal) {
- PRE_REALCALL
- res = ::WritePrivateProfileStringA(
- lpAppName, lpKeyName, lpString,
- ush::string_cast<std::string>(reroute.fileName()).c_str());
- POST_REALCALL
- reroute.updateResult(callContext, res);
-
- if (res && reroute.newReroute())
- reroute.insertMapping(WRITE_CONTEXT());
-
- if (reroute.wasRerouted() || reroute.changedError())
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpKeyName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(reroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-BOOL WINAPI usvfs::hook_WritePrivateProfileStringW(LPCWSTR lpAppName, LPCWSTR lpKeyName,
- LPCWSTR lpString, LPCWSTR lpFileName)
-{
- BOOL res = false;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
-
- if (!callContext.active() || !RerouteW::interestingPath(lpFileName)) {
- res = ::WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, lpFileName);
- callContext.updateLastError();
- return res;
- }
-
- CreateRerouter reroute;
- bool callOriginal = reroute.rerouteNew(READ_CONTEXT(), callContext, lpFileName, true,
- "hook_WritePrivateProfileStringW");
-
- if (callOriginal) {
- PRE_REALCALL
- res = ::WritePrivateProfileStringW(lpAppName, lpKeyName, lpString,
- reroute.fileName());
- POST_REALCALL
- reroute.updateResult(callContext, res);
-
- if (res && reroute.newReroute())
- reroute.insertMapping(WRITE_CONTEXT());
-
- if (reroute.wasRerouted() || reroute.changedError())
- LOG_CALL()
- .PARAM(lpAppName)
- .PARAM(lpKeyName)
- .PARAM(lpFileName)
- .PARAM(reroute.fileName())
- .PARAMHEX(res)
- .PARAM(reroute.originalError())
- .PARAM(callContext.lastError());
- }
-
- HOOK_END
-
- return res;
-}
-
-VOID WINAPI usvfs::hook_ExitProcess(UINT exitCode)
-{
- HOOK_START
-
- {
- HookContext::Ptr context = WRITE_CONTEXT();
-
- std::vector<std::future<int>>& delayed = context->delayed();
-
- if (!delayed.empty()) {
- // ensure all delayed tasks are completed before we exit the process
- for (std::future<int>& delayedOp : delayed) {
- delayedOp.get();
- }
- delayed.clear();
- }
- }
-
- // exitprocess doesn't return so logging the call after the real call doesn't
- // make much sense.
- // nor does any pre/post call macro
- LOG_CALL().PARAM(exitCode);
-
- usvfsDisconnectVFS();
-
- // HookManager::instance().removeHook("ExitProcess");
- // PRE_REALCALL
- ::ExitProcess(exitCode);
- // POST_REALCALL
-
- HOOK_END
-}
diff --git a/libs/usvfs/src/usvfs_dll/hooks/kernel32.h b/libs/usvfs/src/usvfs_dll/hooks/kernel32.h
deleted file mode 100644
index 7087b2e..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/kernel32.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#pragma once
-
-#include "ntdll_declarations.h"
-#include <dllimport.h>
-
-namespace usvfs
-{
-
-DLLEXPORT BOOL WINAPI hook_GetFileAttributesExA(LPCSTR lpFileName,
- GET_FILEEX_INFO_LEVELS fInfoLevelId,
- LPVOID lpFileInformation);
-DLLEXPORT BOOL WINAPI hook_GetFileAttributesExW(LPCWSTR lpFileName,
- GET_FILEEX_INFO_LEVELS fInfoLevelId,
- LPVOID lpFileInformation);
-DLLEXPORT DWORD WINAPI hook_GetFileAttributesA(LPCSTR lpFileName);
-DLLEXPORT DWORD WINAPI hook_GetFileAttributesW(LPCWSTR lpFileName);
-DLLEXPORT DWORD WINAPI hook_SetFileAttributesW(LPCWSTR lpFileName,
- DWORD dwFileAttributes);
-
-DLLEXPORT DWORD WINAPI hook_GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer);
-DLLEXPORT DWORD WINAPI hook_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer);
-DLLEXPORT BOOL WINAPI hook_SetCurrentDirectoryA(LPCSTR lpPathName);
-DLLEXPORT BOOL WINAPI hook_SetCurrentDirectoryW(LPCWSTR lpPathName);
-
-DLLEXPORT DWORD WINAPI hook_GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength,
- LPSTR lpBuffer, LPSTR* lpFilePart);
-DLLEXPORT DWORD WINAPI hook_GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength,
- LPWSTR lpBuffer, LPWSTR* lpFilePart);
-
-DLLEXPORT BOOL WINAPI hook_CreateDirectoryW(LPCWSTR lpPathName,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-DLLEXPORT BOOL WINAPI hook_RemoveDirectoryW(LPCWSTR lpPathName);
-
-DLLEXPORT BOOL WINAPI hook_DeleteFileW(LPCWSTR lpFileName);
-
-DLLEXPORT BOOL WINAPI hook_MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName);
-DLLEXPORT BOOL WINAPI hook_MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName);
-DLLEXPORT BOOL WINAPI hook_MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName,
- DWORD dwFlags);
-DLLEXPORT BOOL WINAPI hook_MoveFileExW(LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName, DWORD dwFlags);
-DLLEXPORT BOOL WINAPI hook_MoveFileWithProgressA(LPCSTR lpExistingFileName,
- LPCSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData, DWORD dwFlags);
-DLLEXPORT BOOL WINAPI hook_MoveFileWithProgressW(LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData, DWORD dwFlags);
-;
-
-DLLEXPORT BOOL WINAPI hook_CopyFileExW(LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine,
- LPVOID lpData, LPBOOL pbCancel,
- DWORD dwCopyFlags);
-
-extern HRESULT(WINAPI* CopyFile2)(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName,
- COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters);
-DLLEXPORT HRESULT WINAPI
-hook_CopyFile2(PCWSTR pwszExistingFileName, PCWSTR pwszNewFileName,
- COPYFILE2_EXTENDED_PARAMETERS* pExtendedParameters);
-
-DLLEXPORT HMODULE WINAPI hook_LoadLibraryExA(LPCSTR lpFileName, HANDLE hFile,
- DWORD dwFlags);
-DLLEXPORT HMODULE WINAPI hook_LoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile,
- DWORD dwFlags);
-
-extern BOOL(WINAPI* CreateProcessInternalW)(
- LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
- LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken);
-DLLEXPORT BOOL WINAPI hook_CreateProcessInternalW(
- LPVOID token, LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
- LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation, LPVOID newToken);
-
-DLLEXPORT DWORD WINAPI hook_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename,
- DWORD nSize);
-DLLEXPORT DWORD WINAPI hook_GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename,
- DWORD nSize);
-
-DLLEXPORT HANDLE WINAPI hook_FindFirstFileExW(
- LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
- FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
-
-DLLEXPORT DWORD WINAPI hook_GetPrivateProfileStringA(LPCSTR lpAppName, LPCSTR lpKeyName,
- LPCSTR lpDefault,
- LPSTR lpReturnedString,
- DWORD nSize, LPCSTR lpFileName);
-DLLEXPORT DWORD WINAPI hook_GetPrivateProfileStringW(LPCWSTR lpAppName,
- LPCWSTR lpKeyName,
- LPCWSTR lpDefault,
- LPWSTR lpReturnedString,
- DWORD nSize, LPCWSTR lpFileName);
-DLLEXPORT DWORD WINAPI hook_GetPrivateProfileSectionA(LPCSTR lpAppName,
- LPSTR lpReturnedString,
- DWORD nSize, LPCSTR lpFileName);
-DLLEXPORT DWORD WINAPI hook_GetPrivateProfileSectionW(LPCWSTR lpAppName,
- LPWSTR lpReturnedString,
- DWORD nSize, LPCWSTR lpFileName);
-DLLEXPORT BOOL WINAPI hook_WritePrivateProfileStringA(LPCSTR lpAppName,
- LPCSTR lpKeyName, LPCSTR lpString,
- LPCSTR lpFileName);
-DLLEXPORT BOOL WINAPI hook_WritePrivateProfileStringW(LPCWSTR lpAppName,
- LPCWSTR lpKeyName,
- LPCWSTR lpString,
- LPCWSTR lpFileName);
-
-DLLEXPORT VOID WINAPI hook_ExitProcess(UINT exitCode);
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp b/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp
deleted file mode 100644
index ad53418..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/ntdll.cpp
+++ /dev/null
@@ -1,1520 +0,0 @@
-#include "ntdll.h"
-
-#include <mutex>
-#include <queue>
-#include <set>
-
-#include <boost/filesystem.hpp>
-
-#include <addrtools.h>
-#include <loghelpers.h>
-#include <stringcast.h>
-#include <stringutils.h>
-#include <unicodestring.h>
-#include <usvfs.h>
-
-#include "../hookcallcontext.h"
-#include "../hookcontext.h"
-#include "../maptracker.h"
-#include "../stringcast_boost.h"
-
-#include "file_information_utils.h"
-#include "sharedids.h"
-
-namespace ulog = usvfs::log;
-namespace ush = usvfs::shared;
-namespace bfs = boost::filesystem;
-
-using usvfs::UnicodeString;
-
-// flag definitions below are copied from winternl.h
-#define FILE_SUPERSEDE 0x00000000
-#define FILE_OPEN 0x00000001
-#define FILE_CREATE 0x00000002
-#define FILE_OPEN_IF 0x00000003
-#define FILE_OVERWRITE 0x00000004
-#define FILE_OVERWRITE_IF 0x00000005
-#define FILE_MAXIMUM_DISPOSITION 0x00000005
-
-template <typename T>
-using unique_ptr_deleter = std::unique_ptr<T, void (*)(T*)>;
-
-class RedirectionInfo
-{
-public:
- UnicodeString path;
- bool redirected;
-
- RedirectionInfo() {}
- RedirectionInfo(UnicodeString path, bool redirected)
- : path(path), redirected(redirected)
- {}
-};
-
-class HandleTracker
-{
-public:
- using handle_type = HANDLE;
- using info_type = UnicodeString;
-
- HandleTracker() { insert_current_directory(); }
-
- info_type lookup(handle_type handle) const
- {
- if (valid_handle(handle)) {
- std::shared_lock<std::shared_mutex> lock(m_mutex);
- auto find = m_map.find(handle);
- if (find != m_map.end())
- return find->second;
- }
- return info_type();
- }
-
- void insert(handle_type handle, const info_type& info)
- {
- if (!valid_handle(handle))
- return;
- std::unique_lock<std::shared_mutex> lock(m_mutex);
- m_map[handle] = info;
- }
-
- void erase(handle_type handle)
- {
- if (!valid_handle(handle))
- return;
- std::unique_lock<std::shared_mutex> lock(m_mutex);
- m_map.erase(handle);
- }
-
-private:
- static bool valid_handle(handle_type handle)
- {
- return handle && handle != INVALID_HANDLE_VALUE;
- }
-
- void insert_current_directory()
- {
- ntdll_declarations_init();
-
- UNICODE_STRING p{0};
- RTL_RELATIVE_NAME r{0};
- NTSTATUS status =
- RtlDosPathNameToRelativeNtPathName_U_WithStatus(L"x", &p, nullptr, &r);
- if (status >= 0) {
- if (r.ContainingDirectory && p.Buffer) {
- size_t len = p.Length / sizeof(WCHAR);
- size_t trim = strlen("\\x") + (p.Buffer[len - 1] ? 0 : 1);
- if (len > trim)
- insert(r.ContainingDirectory, info_type(p.Buffer, len - trim));
- }
- RtlReleaseRelativeName(&r);
- if (p.Buffer)
- HeapFree(GetProcessHeap(), 0, p.Buffer);
- }
- }
-
- mutable std::shared_mutex m_mutex;
- std::unordered_map<handle_type, info_type> m_map;
-};
-
-HandleTracker ntdllHandleTracker;
-
-UnicodeString CreateUnicodeString(const OBJECT_ATTRIBUTES* objectAttributes)
-{
- UnicodeString result = ntdllHandleTracker.lookup(objectAttributes->RootDirectory);
- if (objectAttributes->ObjectName != nullptr) {
- result.appendPath(objectAttributes->ObjectName);
- }
- return result;
-}
-
-std::ostream& operator<<(std::ostream& os, const _UNICODE_STRING& str)
-{
- try {
- // TODO this does not correctly support surrogate pairs
- // since the size used here is the number of 16-bit characters in the buffer
- // whereas
- // toNarrow expects the actual number of characters.
- // It will always underestimate though, so worst case scenario we truncate
- // the string
- os << ush::string_cast<std::string>(str.Buffer, ush::CodePage::UTF8,
- str.Length / sizeof(WCHAR));
- } catch (const std::exception& e) {
- os << e.what();
- }
-
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, POBJECT_ATTRIBUTES attr)
-{
- return operator<<(os, *attr->ObjectName);
-}
-
-RedirectionInfo applyReroute(const usvfs::HookContext::ConstPtr& context,
- const usvfs::HookCallContext& callContext,
- const UnicodeString& inPath)
-{
- RedirectionInfo result;
- result.path = inPath;
- result.redirected = false;
-
- if (callContext.active()) {
- // see if the file exists in the redirection tree
- std::string lookupPath = ush::string_cast<std::string>(
- static_cast<LPCWSTR>(result.path) + 4, ush::CodePage::UTF8);
- auto node = context->redirectionTable()->findNode(lookupPath.c_str());
- // if so, replace the file name with the path to the mapped file
- if ((node.get() != nullptr) &&
- (!node->data().linkTarget.empty() || node->isDirectory())) {
- std::wstring reroutePath;
-
- if (node->data().linkTarget.length() > 0) {
- reroutePath = ush::string_cast<std::wstring>(node->data().linkTarget.c_str(),
- ush::CodePage::UTF8);
- } else {
- reroutePath =
- ush::string_cast<std::wstring>(node->path().c_str(), ush::CodePage::UTF8);
- }
- if ((*reroutePath.rbegin() == L'\\') && (*lookupPath.rbegin() != '\\')) {
- reroutePath.resize(reroutePath.size() - 1);
- }
- std::replace(reroutePath.begin(), reroutePath.end(), L'/', L'\\');
- if (reroutePath[1] == L'\\')
- reroutePath[1] = L'?';
- result.path = LR"(\??\)" + reroutePath;
- result.redirected = true;
- }
- }
- return result;
-}
-
-RedirectionInfo applyReroute(const usvfs::CreateRerouter& rerouter)
-{
- RedirectionInfo result;
- result.path = rerouter.fileName();
- result.redirected = rerouter.wasRerouted();
-
- std::wstring reroutePath(static_cast<LPCWSTR>(result.path));
- std::replace(reroutePath.begin(), reroutePath.end(), L'/', L'\\');
- if (reroutePath[1] == L'\\')
- reroutePath[1] = L'?';
- if (!((reroutePath[0] == L'\\') && (reroutePath[1] == L'?') &&
- (reroutePath[2] == L'?') && (reroutePath[3] == L'\\'))) {
- result.path = LR"(\??\)" + reroutePath;
- }
-
- return result;
-}
-
-struct FindCreateTarget
-{
- usvfs::RedirectionTree::NodePtrT target;
- void operator()(usvfs::RedirectionTree::NodePtrT node)
- {
- if (node->hasFlag(usvfs::shared::FLAG_CREATETARGET)) {
- target = node;
- }
- }
-};
-
-std::pair<UnicodeString, UnicodeString>
-findCreateTarget(const usvfs::HookContext::ConstPtr& context,
- const UnicodeString& inPath)
-{
- std::pair<UnicodeString, UnicodeString> result;
- result.first = inPath;
- result.second = UnicodeString();
-
- std::string lookupPath = ush::string_cast<std::string>(
- static_cast<LPCWSTR>(result.first) + 4, ush::CodePage::UTF8);
- FindCreateTarget visitor;
- usvfs::RedirectionTree::VisitorFunction visitorWrapper =
- [&](const usvfs::RedirectionTree::NodePtrT& node) {
- visitor(node);
- };
- context->redirectionTable()->visitPath(lookupPath, visitorWrapper);
- if (visitor.target.get() != nullptr) {
- bfs::path relativePath =
- ush::make_relative(visitor.target->path(), bfs::path(lookupPath));
-
- bfs::path target(visitor.target->data().linkTarget.c_str());
- target /= relativePath;
-
- result.second = UnicodeString(target.wstring().c_str());
- winapi::ex::wide::createPath(target.parent_path());
- }
- return result;
-}
-
-RedirectionInfo applyReroute(const usvfs::HookContext::ConstPtr& context,
- const usvfs::HookCallContext& callContext,
- POBJECT_ATTRIBUTES inAttributes)
-{
- return applyReroute(context, callContext, CreateUnicodeString(inAttributes));
-}
-
-int NextDividableBy(int number, int divider)
-{
- return static_cast<int>(
- ceilf(static_cast<float>(number) / static_cast<float>(divider)) * divider);
-}
-
-// Something is trying to create a variety of files starting with "\Device\",
-// such as "\Device\DeviceApi\Dev\Query", "\Device\MMCSS\MmThread",
-// "\Device\DeviceApi\CMNotify", etc.
-//
-// This used to create a bunch of spurious "Device" and "MMCSS" folders when
-// using Explorer++. Some of these names seem to part of PnP, others from the
-// "Multimedia Class Scheduler Service".
-//
-// There's basically zero information on this stuff online.
-//
-// Regardless, these files ended up being rerouted, so they became something
-// like C:\game\Data\Somewhere\Device\MMCSS\MmThread, which ended up being
-// rerouted to overwrite and created as a fake path
-// "overwrite\Somewhere\Device\MMCSS"
-//
-// These paths are weird, they feel like pipes or something. It's not clear how
-// they should be reliably recognized, so this is a hardcoded check for any
-// path that starts with "\Device\". These paths will be forwarded directly
-// to NtCreateFile/NtOpenFile
-//
-bool isDeviceFile(std::wstring_view name)
-{
- static const std::wstring_view DevicePrefix(L"\\Device\\");
-
- // starts with
- if (name.size() < DevicePrefix.size()) {
- return false;
- } else {
- return (_wcsnicmp(name.data(), DevicePrefix.data(), DevicePrefix.size()) == 0);
- }
-}
-
-NTSTATUS addNtSearchData(HANDLE hdl, PUNICODE_STRING FileName,
- const std::wstring& fakeName,
- FILE_INFORMATION_CLASS FileInformationClass, PVOID& buffer,
- ULONG& bufferSize, std::set<std::wstring>& foundFiles,
- HANDLE event, PIO_APC_ROUTINE apcRoutine, PVOID apcContext,
- BOOLEAN returnSingleEntry)
-{
- NTSTATUS res = STATUS_NO_SUCH_FILE;
- if (hdl != INVALID_HANDLE_VALUE) {
- PVOID lastValidRecord = nullptr;
- PVOID bufferInit = buffer;
- IO_STATUS_BLOCK status;
- res = NtQueryDirectoryFile(hdl, event, apcRoutine, apcContext, &status, buffer,
- bufferSize, FileInformationClass, returnSingleEntry,
- FileName, FALSE);
-
- if ((res != STATUS_SUCCESS) || (status.Information <= 0)) {
- bufferSize = 0UL;
- } else {
- ULONG totalOffset = 0;
- PVOID lastSkipPos = nullptr;
-
- while (totalOffset < status.Information) {
- ULONG offset;
- std::wstring fileName;
- GetFileInformationData(FileInformationClass, buffer, offset, fileName);
- // in case this is a single-file search result and the specified
- // filename differs from the file name found, replace it in the
- // information structure
- if ((totalOffset == 0) && (offset == 0) && (fakeName.length() > 0)) {
- // if the fake name is larger than what is in the buffer and there is
- // not enough room, that's a buffer overflow
- if ((fakeName.length() > fileName.length()) &&
- ((fakeName.length() - fileName.length()) >
- (bufferSize - status.Information))) {
- res = STATUS_BUFFER_OVERFLOW;
- break;
- }
- // WARNING for the case where the fake name is longer this needs to
- // move back all further results and update the offset first
- SetFileInformationFileName(FileInformationClass, buffer, fakeName);
- fileName = fakeName;
- }
- bool add = true;
- if (fileName.length() > 0) {
- auto insertRes = foundFiles.insert(ush::to_upper(fileName));
- add = insertRes.second; // add only if we didn't find this file before
- }
- if (!add) {
- if (lastSkipPos == nullptr) {
- lastSkipPos = buffer;
- }
- } else {
- if (lastSkipPos != nullptr) {
- memmove(lastSkipPos, buffer, status.Information - totalOffset);
- ULONG delta = static_cast<ULONG>(ush::AddrDiff(buffer, lastSkipPos));
- totalOffset -= delta;
-
- buffer = lastSkipPos;
- lastSkipPos = nullptr;
- }
- lastValidRecord = buffer;
- }
-
- if (offset == 0) {
- offset = static_cast<ULONG>(status.Information) - totalOffset;
- }
- buffer = ush::AddrAdd(buffer, offset);
- totalOffset += offset;
- }
-
- if (lastSkipPos != nullptr) {
- buffer = lastSkipPos;
- bufferSize = static_cast<ULONG>(ush::AddrDiff(buffer, bufferInit));
- // null out the unused rest if there is some
- memset(lastSkipPos, 0, status.Information - bufferSize);
- } else {
- bufferSize = static_cast<ULONG>(ush::AddrDiff(buffer, bufferInit));
- }
- }
- if (lastValidRecord != nullptr) {
- SetFileInformationOffset(FileInformationClass, lastValidRecord, 0);
- }
- }
- return res;
-}
-
-DATA_ID(SearchInfo);
-
-struct Searches
-{
- struct Info
- {
- struct VirtualMatch
- {
- // full path to where the file/directory actually is
- std::wstring realPath;
- // virtual filename (only filename since it has to be within the searched
- // directory)
- // this is left empty when a folder with all its content is mapped to the
- // search directory
- std::wstring virtualName;
- };
-
- Info() : currentSearchHandle(INVALID_HANDLE_VALUE) {}
- std::set<std::wstring> foundFiles;
- HANDLE currentSearchHandle;
- std::queue<VirtualMatch> virtualMatches;
- UnicodeString searchPattern;
- bool regularComplete{false};
- };
-
- Searches() = default;
-
- // must provide a special copy constructor because boost::mutex is
- // non-copyable
- Searches(const Searches& reference) : info(reference.info) {}
-
- Searches& operator=(const Searches&) = delete;
-
- std::recursive_mutex queryMutex;
-
- std::map<HANDLE, Info> info;
-};
-
-void gatherVirtualEntries(const UnicodeString& dirName,
- const usvfs::RedirectionTreeContainer& redir,
- PUNICODE_STRING FileName, Searches::Info& info)
-{
- LPCWSTR dirNameW = static_cast<LPCWSTR>(dirName);
- // fix directory name. I'd love to know why microsoft sometimes uses "\??\" vs
- // "\\?\"
- if ((wcsncmp(dirNameW, LR"(\\?\)", 4) == 0) ||
- (wcsncmp(dirNameW, LR"(\??\)", 4) == 0)) {
- dirNameW += 4;
- }
- auto node = redir->findNode(boost::filesystem::path(dirNameW));
- if (node.get() != nullptr) {
- std::string searchPattern =
- FileName != nullptr
- ? ush::string_cast<std::string>(FileName->Buffer, ush::CodePage::UTF8)
- : "*.*";
-
- boost::replace_all(searchPattern, "\"", ".");
-
- for (const auto& subNode : node->find(searchPattern)) {
- if (((subNode->data().linkTarget.length() > 0) || subNode->isDirectory()) &&
- !subNode->hasFlag(usvfs::shared::FLAG_DUMMY)) {
- std::wstring vName =
- ush::string_cast<std::wstring>(subNode->name(), ush::CodePage::UTF8);
-
- Searches::Info::VirtualMatch m;
- if (subNode->data().linkTarget.length() > 0) {
- m = {ush::string_cast<std::wstring>(subNode->data().linkTarget.c_str(),
- ush::CodePage::UTF8),
- vName};
- } else {
- m = {ush::string_cast<std::wstring>(subNode->path().c_str(),
- ush::CodePage::UTF8),
- vName};
- }
-
- info.virtualMatches.push(m);
- info.foundFiles.insert(ush::to_upper(vName));
- }
- }
- }
-}
-
-/**
- * @brief insert a virtual entry into the search result
- * @param FileInformation
- * @param FileInformationClass
- * @param info
- * @param realPath path were the actual file resides
- * @param virtualName virtual file name (without path). will often be the same
- * as the name component of realpath
- * @param ReturnSingleEntry
- * @param dataRead
- * @return true if a virtual result was added, false if the search handle in the
- * info object yields no more results
- */
-bool addVirtualSearchResult(PVOID& FileInformation,
- FILE_INFORMATION_CLASS FileInformationClass,
- Searches::Info& info, const std::wstring& realPath,
- const std::wstring& virtualName, BOOLEAN ReturnSingleEntry,
- ULONG& dataRead)
-{
- // this opens a search in the real location, then copies the information about
- // files we care about (the ones being mapped) to the result we intend to
- // return
- bfs::path fullPath(realPath);
- if (fullPath.filename().wstring() == L".") {
- fullPath = fullPath.parent_path();
- }
- if (info.currentSearchHandle == INVALID_HANDLE_VALUE) {
- std::wstring dirName = fullPath.parent_path().wstring();
- if (dirName.length() >= MAX_PATH && !ush::startswith(dirName.c_str(), LR"(\\?\)"))
- dirName = LR"(\\?\)" + dirName;
- info.currentSearchHandle =
- CreateFileW(dirName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
- }
- std::wstring fileName =
- ush::string_cast<std::wstring>(fullPath.filename().string(), ush::CodePage::UTF8);
- NTSTATUS subRes = addNtSearchData(
- info.currentSearchHandle,
- (fileName != L".") ? static_cast<PUNICODE_STRING>(UnicodeString(fileName.c_str()))
- : nullptr,
- virtualName, FileInformationClass, FileInformation, dataRead, info.foundFiles,
- nullptr, nullptr, nullptr, ReturnSingleEntry);
- if (subRes == STATUS_SUCCESS) {
- return true;
- } else {
- // STATUS_NO_MORE_FILES merely means the search ended, everything else is an
- // error message. Either way, the search here is finished and we should
- // resume in the next mapped directory
- if (subRes != STATUS_NO_MORE_FILES) {
- spdlog::get("hooks")->warn("error reported listing files in {0}: {1:x}",
- fullPath.string(), static_cast<uint32_t>(subRes));
- }
- return false;
- }
-}
-
-NTSTATUS WINAPI usvfs::hook_NtQueryDirectoryFile(
- HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
- PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
- FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry,
- PUNICODE_STRING FileName, BOOLEAN RestartScan)
-{
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- // this is quite messy...
- // first, this will gather the virtual locations mapping to the iterated one
- // then we return results from the real location, skipping those that exist
- // in the virtual locations, as those take precedence
- // finally the virtual results are returned, adding each result to a skip
- // list, so they don't get added twice
- //
- // if we don't add the regular files first, "." and ".." wouldn't be in the
- // first search result of wildcard searches which may confuse the caller
- NTSTATUS res = STATUS_NO_MORE_FILES;
- HOOK_START_GROUP(MutExHookGroup::FIND_FILES)
- if (!callContext.active()) {
- return ::NtQueryDirectoryFile(
- FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation,
- Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);
- }
-
- // std::unique_lock<std::recursive_mutex> queryLock;
- std::map<HANDLE, Searches::Info>::iterator infoIter;
- bool firstSearch = false;
-
- { // scope to limit context lifetime
- HookContext::ConstPtr context = READ_CONTEXT();
- Searches& activeSearches = context->customData<Searches>(SearchInfo);
- // queryLock = std::unique_lock<std::recursive_mutex>(activeSearches.queryMutex);
-
- if (RestartScan) {
- auto iter = activeSearches.info.find(FileHandle);
- if (iter != activeSearches.info.end()) {
- activeSearches.info.erase(iter);
- }
- }
-
- // see if we already have a running search
- infoIter = activeSearches.info.find(FileHandle);
- firstSearch = (infoIter == activeSearches.info.end());
- }
-
- if (firstSearch) {
- HookContext::Ptr context = WRITE_CONTEXT();
- Searches& activeSearches = context->customData<Searches>(SearchInfo);
- // tradeoff time: we store this search status even if no virtual results
- // were found. This causes a little extra cost here and in NtClose every
- // time a non-virtual dir is being searched. However if we don't,
- // whenever NtQueryDirectoryFile is called another time on the same handle,
- // this (expensive) block would be run again.
- infoIter =
- activeSearches.info.insert(std::make_pair(FileHandle, Searches::Info())).first;
- infoIter->second.searchPattern.appendPath(FileName);
-
- SearchHandleMap& searchMap = context->customData<SearchHandleMap>(SearchHandles);
- SearchHandleMap::iterator iter = searchMap.find(FileHandle);
-
- UnicodeString searchPath;
- if (iter != searchMap.end()) {
- searchPath = UnicodeString(iter->second.c_str());
- infoIter->second.currentSearchHandle = CreateFileW(
- iter->second.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
- } else {
- searchPath = ntdllHandleTracker.lookup(FileHandle);
- }
- gatherVirtualEntries(searchPath, context->redirectionTable(), FileName,
- infoIter->second);
- }
-
- ULONG dataRead = Length;
- PVOID FileInformationCurrent = FileInformation;
-
- // add regular search results, skipping those files we have in a virtual
- // location
- bool moreRegular = !infoIter->second.regularComplete;
- bool dataReturned = false;
- while (moreRegular && !dataReturned) {
- dataRead = Length;
-
- HANDLE handle = infoIter->second.currentSearchHandle;
- if (handle == INVALID_HANDLE_VALUE) {
- handle = FileHandle;
- }
- NTSTATUS subRes = addNtSearchData(
- handle, FileName, L"", FileInformationClass, FileInformationCurrent, dataRead,
- infoIter->second.foundFiles, Event, ApcRoutine, ApcContext, ReturnSingleEntry);
- moreRegular = subRes == STATUS_SUCCESS;
- if (moreRegular) {
- dataReturned = dataRead != 0;
- } else {
- infoIter->second.regularComplete = true;
- infoIter->second.foundFiles.clear();
- if (infoIter->second.currentSearchHandle != INVALID_HANDLE_VALUE) {
- ::CloseHandle(infoIter->second.currentSearchHandle);
- infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE;
- }
- }
- }
- if (!moreRegular) {
- // add virtual results
- while (!dataReturned && infoIter->second.virtualMatches.size() > 0) {
- auto match = infoIter->second.virtualMatches.front();
- if (match.realPath.size() != 0) {
- dataRead = Length;
- if (addVirtualSearchResult(FileInformationCurrent, FileInformationClass,
- infoIter->second, match.realPath, match.virtualName,
- ReturnSingleEntry, dataRead)) {
- // a positive result here means the call returned data and there may
- // be further objects to be retrieved by repeating the call
- dataReturned = true;
- } else {
- // proceed to next search handle
-
- // TODO: doesn't append search results from more than one redirection
- // per call. This is bad for performance but otherwise we'd need to
- // re-write the offsets between information objects
- infoIter->second.virtualMatches.pop();
- CloseHandle(infoIter->second.currentSearchHandle);
- infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE;
- }
- }
- }
- }
-
- if (!dataReturned) {
- if (firstSearch) {
- res = STATUS_NO_SUCH_FILE;
- } else {
- res = STATUS_NO_MORE_FILES;
- }
- } else {
- res = STATUS_SUCCESS;
- }
- IoStatusBlock->Status = res;
- IoStatusBlock->Information = dataRead;
-
- size_t numVirtualFiles = infoIter->second.virtualMatches.size();
- if ((numVirtualFiles > 0)) {
- LOG_CALL()
- .addParam("path", ntdllHandleTracker.lookup(FileHandle))
- .PARAM(FileInformationClass)
- .PARAM(FileName)
- .PARAM(numVirtualFiles)
- .PARAMWRAP(res);
- }
-
- HOOK_END
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtQueryDirectoryFileEx(
- HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
- PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
- FILE_INFORMATION_CLASS FileInformationClass, ULONG QueryFlags,
- PUNICODE_STRING FileName)
-{
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
- NTSTATUS res = STATUS_NO_MORE_FILES;
-
- // this is quite messy...
- // first, this will gather the virtual locations mapping to the iterated one
- // then we return results from the real location, skipping those that exist
- // in the virtual locations, as those take precedence
- // finally the virtual results are returned, adding each result to a skip
- // list, so they don't get added twice
- //
- // if we don't add the regular files first, "." and ".." wouldn't be in the
- // first search result of wildcard searches which may confuse the caller
- HOOK_START_GROUP(MutExHookGroup::FIND_FILES)
- if (!callContext.active()) {
- return ::NtQueryDirectoryFileEx(FileHandle, Event, ApcRoutine, ApcContext,
- IoStatusBlock, FileInformation, Length,
- FileInformationClass, QueryFlags, FileName);
- }
-
- // std::unique_lock<std::recursive_mutex> queryLock;
- std::map<HANDLE, Searches::Info>::iterator infoIter;
- bool firstSearch = false;
-
- { // scope to limit context lifetime
- HookContext::ConstPtr context = READ_CONTEXT();
- Searches& activeSearches = context->customData<Searches>(SearchInfo);
- // queryLock = std::unique_lock<std::recursive_mutex>(activeSearches.queryMutex);
-
- if (QueryFlags & SL_RESTART_SCAN) {
- auto iter = activeSearches.info.find(FileHandle);
- if (iter != activeSearches.info.end()) {
- activeSearches.info.erase(iter);
- }
- }
-
- // see if we already have a running search
- infoIter = activeSearches.info.find(FileHandle);
- firstSearch = (infoIter == activeSearches.info.end());
- }
-
- if (firstSearch) {
- HookContext::Ptr context = WRITE_CONTEXT();
- Searches& activeSearches = context->customData<Searches>(SearchInfo);
- // tradeoff time: we store this search status even if no virtual results
- // were found. This causes a little extra cost here and in NtClose every
- // time a non-virtual dir is being searched. However if we don't,
- // whenever NtQueryDirectoryFile is called another time on the same handle,
- // this (expensive) block would be run again.
- infoIter =
- activeSearches.info.insert(std::make_pair(FileHandle, Searches::Info())).first;
- infoIter->second.searchPattern.appendPath(FileName);
-
- SearchHandleMap& searchMap = context->customData<SearchHandleMap>(SearchHandles);
- SearchHandleMap::iterator iter = searchMap.find(FileHandle);
-
- UnicodeString searchPath;
- if (iter != searchMap.end()) {
- searchPath = UnicodeString(iter->second.c_str());
- infoIter->second.currentSearchHandle = CreateFileW(
- iter->second.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
- } else {
- searchPath = ntdllHandleTracker.lookup(FileHandle);
- }
- gatherVirtualEntries(searchPath, context->redirectionTable(), FileName,
- infoIter->second);
- }
-
- ULONG dataRead = Length;
- PVOID FileInformationCurrent = FileInformation;
-
- // add regular search results, skipping those files we have in a virtual
- // location
- bool moreRegular = !infoIter->second.regularComplete;
- bool dataReturned = false;
- while (moreRegular && !dataReturned) {
- dataRead = Length;
-
- HANDLE handle = infoIter->second.currentSearchHandle;
- if (handle == INVALID_HANDLE_VALUE) {
- handle = FileHandle;
- }
- NTSTATUS subRes = addNtSearchData(handle, FileName, L"", FileInformationClass,
- FileInformationCurrent, dataRead,
- infoIter->second.foundFiles, Event, ApcRoutine,
- ApcContext, QueryFlags & SL_RETURN_SINGLE_ENTRY);
- moreRegular = subRes == STATUS_SUCCESS;
- if (moreRegular) {
- dataReturned = dataRead != 0;
- } else {
- infoIter->second.regularComplete = true;
- infoIter->second.foundFiles.clear();
- if (infoIter->second.currentSearchHandle != INVALID_HANDLE_VALUE) {
- ::CloseHandle(infoIter->second.currentSearchHandle);
- infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE;
- }
- }
- }
- if (!moreRegular) {
- // add virtual results
- while (!dataReturned && infoIter->second.virtualMatches.size() > 0) {
- auto match = infoIter->second.virtualMatches.front();
- if (match.realPath.size() != 0) {
- dataRead = Length;
- if (addVirtualSearchResult(FileInformationCurrent, FileInformationClass,
- infoIter->second, match.realPath, match.virtualName,
- QueryFlags & SL_RETURN_SINGLE_ENTRY, dataRead)) {
- // a positive result here means the call returned data and there may
- // be further objects to be retrieved by repeating the call
- dataReturned = true;
- } else {
- // proceed to next search handle
-
- // TODO: doesn't append search results from more than one redirection
- // per call. This is bad for performance but otherwise we'd need to
- // re-write the offsets between information objects
- infoIter->second.virtualMatches.pop();
- CloseHandle(infoIter->second.currentSearchHandle);
- infoIter->second.currentSearchHandle = INVALID_HANDLE_VALUE;
- }
- }
- }
- }
-
- if (!dataReturned) {
- if (firstSearch) {
- res = STATUS_NO_SUCH_FILE;
- } else {
- res = STATUS_NO_MORE_FILES;
- }
- } else {
- res = STATUS_SUCCESS;
- }
- IoStatusBlock->Status = res;
- IoStatusBlock->Information = dataRead;
-
- size_t numVirtualFiles = infoIter->second.virtualMatches.size();
- if ((numVirtualFiles > 0)) {
- LOG_CALL()
- .addParam("path", ntdllHandleTracker.lookup(FileHandle))
- .PARAM(FileInformationClass)
- .PARAM(FileName)
- .PARAM(QueryFlags)
- .PARAM(numVirtualFiles)
- .PARAMWRAP(res);
- }
-
- HOOK_END
- return res;
-}
-
-DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryObject(
- HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass,
- PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength)
-{
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active()) {
- return ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation,
- ObjectInformationLength, ReturnLength);
- }
-
- PRE_REALCALL
- res = ::NtQueryObject(Handle, ObjectInformationClass, ObjectInformation,
- ObjectInformationLength, ReturnLength);
- POST_REALCALL
-
- // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be
- // smaller than the original one
- //
- // we only handle STATUS_INFO_LENGTH_MISMATCH if ReturnLength is not NULL since
- // this is only returned if the length is too small to hold the structure itself
- // (regardless of the name), in which case, we need to compute our own ReturnLength
- //
- if ((res == STATUS_SUCCESS || res == STATUS_BUFFER_OVERFLOW ||
- (res == STATUS_INFO_LENGTH_MISMATCH && ReturnLength)) &&
- (ObjectInformationClass == ObjectNameInformation)) {
- const auto trackerInfo = ntdllHandleTracker.lookup(Handle);
- const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo);
-
- OBJECT_NAME_INFORMATION* info =
- reinterpret_cast<OBJECT_NAME_INFORMATION*>(ObjectInformation);
-
- if (redir.redirected) {
- // https://learn.microsoft.com/en-us/windows/win32/fileio/displaying-volume-paths
- //
-
- // TODO: is that always true?
- // path should start with \??\X: - we need to replace this by device name
- //
- WCHAR deviceName[MAX_PATH];
- std::wstring buffer(static_cast<LPCWSTR>(trackerInfo));
- buffer[6] = L'\0';
-
- QueryDosDeviceW(buffer.data() + 4, deviceName, ARRAYSIZE(deviceName));
-
- buffer = std::wstring(deviceName) + L'\\' +
- std::wstring(buffer.data() + 7, buffer.size() - 7);
-
- // the name is put in the buffer AFTER the struct, so the required size if
- // sizeof(OBJECT_NAME_INFORMATION) + the number of bytes for the name + 2 bytes
- // for a wide null character
- const auto requiredLength =
- sizeof(OBJECT_NAME_INFORMATION) + buffer.size() * 2 + 2;
- if (ObjectInformationLength < requiredLength) {
- // if the status was info length mismatch, we keep it, we are just going to
- // update *ReturnLength
- if (res != STATUS_INFO_LENGTH_MISMATCH) {
- res = STATUS_BUFFER_OVERFLOW;
- }
-
- if (ReturnLength) {
- *ReturnLength = static_cast<ULONG>(requiredLength);
- }
- } else {
- // put the unicode buffer at the end of the object
- const USHORT unicodeBufferLength = static_cast<USHORT>(std::min(
- static_cast<unsigned long long>(std::numeric_limits<USHORT>::max()),
- static_cast<unsigned long long>(ObjectInformationLength -
- sizeof(OBJECT_NAME_INFORMATION))));
- LPWSTR unicodeBuffer = reinterpret_cast<LPWSTR>(
- static_cast<LPSTR>(ObjectInformation) + sizeof(OBJECT_NAME_INFORMATION));
-
- // copy the path into the buffer
- wmemcpy_s(unicodeBuffer, unicodeBufferLength, buffer.data(), buffer.size());
-
- // set the null character
- unicodeBuffer[buffer.size()] = L'\0';
-
- // update the actual unicode string
- info->Name.Buffer = unicodeBuffer;
- info->Name.Length = static_cast<USHORT>(buffer.size() * 2);
- info->Name.MaximumLength = unicodeBufferLength;
-
- res = STATUS_SUCCESS;
- }
- }
-
- auto logger = LOG_CALL()
- .PARAMWRAP(res)
- .PARAM(ObjectInformationLength)
- .addParam("return_length", ReturnLength ? *ReturnLength : -1)
- .addParam("tracker_path", trackerInfo)
- .PARAM(ObjectInformationClass)
- .PARAM(redir.redirected)
- .PARAM(redir.path);
-
- if (res == STATUS_SUCCESS) {
- logger.addParam("name_info", info->Name);
- } else {
- logger.addParam("name_info", "");
- }
- }
-
- HOOK_END
- return res;
-}
-
-DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationFile(
- HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
- ULONG Length, FILE_INFORMATION_CLASS FileInformationClass)
-{
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- if (!callContext.active()) {
- return ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, Length,
- FileInformationClass);
- }
-
- PRE_REALCALL
- res = ::NtQueryInformationFile(FileHandle, IoStatusBlock, FileInformation, Length,
- FileInformationClass);
- POST_REALCALL
-
- // we handle both SUCCESS and BUFFER_OVERFLOW since the fixed name might be
- // smaller than the original one
- //
- // we do not handle STATUS_INFO_LENGTH_MISMATCH because this is only returned if
- // the length is too small to old the structure itself (regardless of the name)
- //
- // TODO: currently, this does not handle STATUS_BUFFER_OVERLOW for ALL information
- // because most of the structures would need to be manually filled, which is very
- // complicated - this should not pose huge issue
- //
- if (((res == STATUS_SUCCESS || res == STATUS_BUFFER_OVERFLOW) &&
- (FileInformationClass == FileNameInformation ||
- FileInformationClass == FileNormalizedNameInformation)) ||
- (res == STATUS_SUCCESS && FileInformationClass == FileAllInformation)) {
-
- const auto trackerInfo = ntdllHandleTracker.lookup(FileHandle);
- const auto redir = applyReroute(READ_CONTEXT(), callContext, trackerInfo);
-
- // TODO: difference between FileNameInformation and FileNormalizedNameInformation
-
- // maximum length in bytes - the required length is
- // - for ALL, 100 + the number of bytes in the name (not account for null character)
- // - for NAME, 4 + the number of bytes in the name (not accounting for null
- // character)
- //
- // it is close to the sizeof the structure + the number of bytes in the name, except
- // for the alignment that gives us a bit more space
- //
- ULONG prefixStructLength;
- FILE_NAME_INFORMATION* info;
- if (FileInformationClass == FileAllInformation) {
- info = &reinterpret_cast<FILE_ALL_INFORMATION*>(FileInformation)->NameInformation;
- prefixStructLength = sizeof(FILE_ALL_INFORMATION) - 4;
- } else {
- info = reinterpret_cast<FILE_NAME_INFORMATION*>(FileInformation);
- prefixStructLength = sizeof(FILE_NAME_INFORMATION) - 4;
- }
-
- if (redir.redirected) {
- auto requiredLength = prefixStructLength + 2 * (trackerInfo.size() - 6);
- if (Length < requiredLength) {
- res = STATUS_BUFFER_OVERFLOW;
- } else {
- // strip the \??\X: prefix (X being the drive name)
- LPCWSTR filenameFixed = static_cast<LPCWSTR>(trackerInfo) + 6;
-
- // not using SetInfoFilename because the length is not set and we do not need to
- // 0-out the memory here
- info->FileNameLength = static_cast<ULONG>((trackerInfo.size() - 6) * 2);
- wmemcpy(info->FileName, filenameFixed, trackerInfo.size() - 6);
- res = STATUS_SUCCESS;
-
- // update status block, Information is the number of bytes written, basically
- // the required length
- IoStatusBlock->Status = STATUS_SUCCESS;
- IoStatusBlock->Information = static_cast<ULONG_PTR>(requiredLength);
- }
- }
-
- LOG_CALL()
- .PARAMWRAP(res)
- .addParam("tracker_path", trackerInfo)
- .PARAM(FileInformationClass)
- .PARAM(redir.redirected)
- .PARAM(redir.path)
- .addParam("name_info", res == STATUS_SUCCESS
- ? std::wstring{info->FileName,
- info->FileNameLength / sizeof(WCHAR)}
- : std::wstring{});
- }
-
- HOOK_END
- return res;
-}
-
-unique_ptr_deleter<OBJECT_ATTRIBUTES>
-makeObjectAttributes(RedirectionInfo& redirInfo, POBJECT_ATTRIBUTES attributeTemplate)
-{
- if (redirInfo.redirected) {
- unique_ptr_deleter<OBJECT_ATTRIBUTES> result(new OBJECT_ATTRIBUTES,
- [](OBJECT_ATTRIBUTES* ptr) {
- delete ptr;
- });
- memcpy(result.get(), attributeTemplate, sizeof(OBJECT_ATTRIBUTES));
- result->RootDirectory = nullptr;
- result->ObjectName = static_cast<PUNICODE_STRING>(redirInfo.path);
- return result;
- } else {
- // just reuse the template with a dummy deleter
- return unique_ptr_deleter<OBJECT_ATTRIBUTES>(attributeTemplate,
- [](OBJECT_ATTRIBUTES*) {});
- }
-}
-
-DLLEXPORT NTSTATUS WINAPI usvfs::hook_NtQueryInformationByName(
- POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
- PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass)
-{
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
-
- if (!callContext.active()) {
- res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation,
- Length, FileInformationClass);
- callContext.updateLastError();
- return res;
- }
-
- RedirectionInfo redir =
- applyReroute(READ_CONTEXT(), callContext, CreateUnicodeString(ObjectAttributes));
-
- if (redir.redirected) {
- auto newObjectAttributes = makeObjectAttributes(redir, ObjectAttributes);
-
- PRE_REALCALL
- res = ::NtQueryInformationByName(newObjectAttributes.get(), IoStatusBlock,
- FileInformation, Length, FileInformationClass);
- POST_REALCALL
-
- LOG_CALL()
- .PARAMWRAP(res)
- .addParam("input_path", *ObjectAttributes->ObjectName)
- .addParam("reroute_path", redir.path);
- } else {
- PRE_REALCALL
- res = ::NtQueryInformationByName(ObjectAttributes, IoStatusBlock, FileInformation,
- Length, FileInformationClass);
- POST_REALCALL
- }
-
- HOOK_END
- return res;
-}
-
-NTSTATUS ntdll_mess_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess,
- ULONG OpenOptions)
-{
- using namespace usvfs;
-
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- NTSTATUS res = STATUS_NO_SUCH_FILE;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
- // Why is the usual if (!callContext.active()... check missing?
-
- bool storePath = false;
- if (((OpenOptions & FILE_DIRECTORY_FILE) != 0UL) &&
- ((OpenOptions & FILE_OPEN_FOR_BACKUP_INTENT) != 0UL)) {
- // this may be an attempt to open a directory handle for iterating.
- // If so we need to treat it a little bit differently
- /* usvfs::FunctionGroupLock lock(usvfs::MutExHookGroup::FILE_ATTRIBUTES);
- FILE_BASIC_INFORMATION dummy;
- storePath = FAILED(NtQueryAttributesFile(ObjectAttributes, &dummy));*/
- storePath = true;
- }
-
- UnicodeString fullName = CreateUnicodeString(ObjectAttributes);
-
- if (isDeviceFile(static_cast<LPCWSTR>(fullName))) {
- return ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- ShareAccess, OpenOptions);
- }
-
- UnicodeString Path = ntdllHandleTracker.lookup(ObjectAttributes->RootDirectory);
-
- std::wstring checkpath =
- ush::string_cast<std::wstring>(static_cast<LPCWSTR>(Path), ush::CodePage::UTF8);
-
- if ((fullName.size() == 0) ||
- (GetFileSize(ObjectAttributes->RootDirectory, nullptr) != INVALID_FILE_SIZE)) {
- // //relative paths that we don't have permission over will fail here due that we
- // can't get the filesize of the root directory
- // //We should try again to see if it is a directory using another method
- if ((fullName.size() == 0) ||
- (GetFileAttributesW((LPCWSTR)checkpath.c_str()) == INVALID_FILE_ATTRIBUTES)) {
- return ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- ShareAccess, OpenOptions);
- }
- }
-
- try {
- RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, fullName);
- unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes =
- makeObjectAttributes(redir, ObjectAttributes);
-
- PRE_REALCALL
- res = ::NtOpenFile(FileHandle, DesiredAccess, adjustedAttributes.get(),
- IoStatusBlock, ShareAccess, OpenOptions);
- POST_REALCALL
- if (SUCCEEDED(res) && storePath) {
- // store the original search path for use during iteration
- READ_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[*FileHandle] =
- static_cast<LPCWSTR>(fullName);
-#pragma message("need to clean up this handle in CloseHandle call")
- }
-
- if (redir.redirected) {
- LOG_CALL()
- .addParam("source", ObjectAttributes)
- .addParam("rerouted", adjustedAttributes.get())
- .PARAM(*FileHandle)
- .PARAM(OpenOptions)
- .PARAMWRAP(res);
- }
- } catch (const std::exception&) {
- PRE_REALCALL
- res = ::NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- ShareAccess, OpenOptions);
- POST_REALCALL
- }
- HOOK_END
-
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock,
- ULONG ShareAccess, ULONG OpenOptions)
-{
- NTSTATUS res = ntdll_mess_NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes,
- IoStatusBlock, ShareAccess, OpenOptions);
- if (res >= 0 && ObjectAttributes && FileHandle &&
- GetFileType(*FileHandle) == FILE_TYPE_DISK)
- ntdllHandleTracker.insert(*FileHandle, CreateUnicodeString(ObjectAttributes));
- return res;
-}
-
-NTSTATUS ntdll_mess_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock,
- PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
- ULONG ShareAccess, ULONG CreateDisposition,
- ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength)
-{
- using namespace usvfs;
-
- NTSTATUS res = STATUS_NO_SUCH_FILE;
-
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- HOOK_START_GROUP(MutExHookGroup::OPEN_FILE)
- if (!callContext.active()) {
- return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- AllocationSize, FileAttributes, ShareAccess,
- CreateDisposition, CreateOptions, EaBuffer, EaLength);
- }
-
- UnicodeString inPath = CreateUnicodeString(ObjectAttributes);
- LPCWSTR inPathW = static_cast<LPCWSTR>(inPath);
-
- if (inPath.size() == 0) {
- spdlog::get("hooks")->info(
- "failed to set from handle: {0}",
- ush::string_cast<std::string>(ObjectAttributes->ObjectName->Buffer));
- return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- AllocationSize, FileAttributes, ShareAccess,
- CreateDisposition, CreateOptions, EaBuffer, EaLength);
- }
-
- if (isDeviceFile(inPathW)) {
- return ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- AllocationSize, FileAttributes, ShareAccess,
- CreateDisposition, CreateOptions, EaBuffer, EaLength);
- }
-
- DWORD convertedDisposition = OPEN_EXISTING;
- switch (CreateDisposition) {
- case FILE_SUPERSEDE:
- convertedDisposition = CREATE_ALWAYS;
- break;
- case FILE_OPEN:
- convertedDisposition = OPEN_EXISTING;
- break;
- case FILE_CREATE:
- convertedDisposition = CREATE_NEW;
- break;
- case FILE_OPEN_IF:
- convertedDisposition = OPEN_ALWAYS;
- break;
- case FILE_OVERWRITE:
- convertedDisposition = TRUNCATE_EXISTING;
- break;
- case FILE_OVERWRITE_IF:
- convertedDisposition = CREATE_ALWAYS;
- break;
- default:
- spdlog::get("hooks")->error("invalid disposition: {0}", CreateDisposition);
- break;
- }
-
- DWORD convertedAccess = 0;
- if ((DesiredAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ)
- convertedAccess |= GENERIC_READ;
- if ((DesiredAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE)
- convertedAccess |= GENERIC_WRITE;
- if ((DesiredAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE)
- convertedAccess |= GENERIC_EXECUTE;
- if ((DesiredAccess & FILE_ALL_ACCESS) == FILE_ALL_ACCESS)
- convertedAccess |= GENERIC_ALL;
-
- ULONG originalDisposition = CreateDisposition;
- CreateRerouter rerouter;
- if (rerouter.rerouteCreate(
- READ_CONTEXT(), callContext, inPathW, convertedDisposition, convertedAccess,
- (LPSECURITY_ATTRIBUTES)ObjectAttributes->SecurityDescriptor)) {
- switch (convertedDisposition) {
- case CREATE_NEW:
- CreateDisposition = FILE_CREATE;
- break;
- case CREATE_ALWAYS:
- if (CreateDisposition != FILE_SUPERSEDE)
- CreateDisposition = FILE_OVERWRITE_IF;
- break;
- case OPEN_EXISTING:
- CreateDisposition = FILE_OPEN;
- break;
- case OPEN_ALWAYS:
- CreateDisposition = FILE_OPEN_IF;
- break;
- case TRUNCATE_EXISTING:
- CreateDisposition = FILE_OVERWRITE;
- break;
- }
-
- RedirectionInfo redir = applyReroute(rerouter);
-
- unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes =
- makeObjectAttributes(redir, ObjectAttributes);
-
- PRE_REALCALL
- res = ::NtCreateFile(FileHandle, DesiredAccess, adjustedAttributes.get(),
- IoStatusBlock, AllocationSize, FileAttributes, ShareAccess,
- CreateDisposition, CreateOptions, EaBuffer, EaLength);
- POST_REALCALL
- rerouter.updateResult(callContext, res == STATUS_SUCCESS);
-
- if (res == STATUS_SUCCESS) {
- if (rerouter.newReroute())
- rerouter.insertMapping(WRITE_CONTEXT());
-
- if (rerouter.isDir() && rerouter.wasRerouted() &&
- ((FileAttributes & FILE_OPEN_FOR_BACKUP_INTENT) ==
- FILE_OPEN_FOR_BACKUP_INTENT)) {
- // store the original search path for use during iteration
- WRITE_CONTEXT()->customData<SearchHandleMap>(SearchHandles)[*FileHandle] =
- inPathW;
- }
- }
-
- if (rerouter.wasRerouted() || rerouter.changedError() ||
- originalDisposition != CreateDisposition) {
- LOG_CALL()
- .PARAM(inPathW)
- .PARAM(rerouter.fileName())
- .PARAMHEX(DesiredAccess)
- .PARAMHEX(originalDisposition)
- .PARAMHEX(CreateDisposition)
- .PARAMHEX(FileAttributes)
- .PARAMHEX(res)
- .PARAMHEX(*FileHandle)
- .PARAM(rerouter.originalError())
- .PARAM(rerouter.error());
- }
- } else {
- // make the original call to set up the proper errors and return statuses
- PRE_REALCALL
- res = ::NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
- AllocationSize, FileAttributes, ShareAccess, CreateDisposition,
- CreateOptions, EaBuffer, EaLength);
- POST_REALCALL
- }
-
- HOOK_END
-
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock,
- PLARGE_INTEGER AllocationSize,
- ULONG FileAttributes, ULONG ShareAccess,
- ULONG CreateDisposition, ULONG CreateOptions,
- PVOID EaBuffer, ULONG EaLength)
-{
- NTSTATUS res = ntdll_mess_NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes,
- IoStatusBlock, AllocationSize, FileAttributes,
- ShareAccess, CreateDisposition, CreateOptions,
- EaBuffer, EaLength);
- if (res >= 0 && ObjectAttributes && FileHandle &&
- GetFileType(*FileHandle) == FILE_TYPE_DISK)
- ntdllHandleTracker.insert(*FileHandle, CreateUnicodeString(ObjectAttributes));
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtClose(HANDLE Handle)
-{
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- NTSTATUS res = STATUS_NO_SUCH_FILE;
-
- HOOK_START_GROUP(MutExHookGroup::ALL_GROUPS)
- bool log = false;
-
- if ((::GetFileType(Handle) == FILE_TYPE_DISK)) {
- HookContext::Ptr context = WRITE_CONTEXT();
-
- { // clean up search data associated with this handle part 1
- Searches& activeSearches = context->customData<Searches>(SearchInfo);
- // std::lock_guard<std::recursive_mutex> lock(activeSearches.queryMutex);
- auto iter = activeSearches.info.find(Handle);
- if (iter != activeSearches.info.end()) {
- if (iter->second.currentSearchHandle != INVALID_HANDLE_VALUE) {
- ::CloseHandle(iter->second.currentSearchHandle);
- }
-
- activeSearches.info.erase(iter);
- log = true;
- }
- }
-
- {
- SearchHandleMap& searchHandles =
- context->customData<SearchHandleMap>(SearchHandles);
- auto iter = searchHandles.find(Handle);
- if (iter != searchHandles.end()) {
- searchHandles.erase(iter);
- log = true;
- }
- }
- }
-
- if (GetFileType(Handle) == FILE_TYPE_DISK)
- ntdllHandleTracker.erase(Handle);
-
- PRE_REALCALL
- res = ::NtClose(Handle);
- POST_REALCALL
-
- if (log) {
- LOG_CALL().PARAM(Handle).PARAMWRAP(res);
- }
-
- HOOK_END
-
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtQueryAttributesFile(
- POBJECT_ATTRIBUTES ObjectAttributes, PFILE_BASIC_INFORMATION FileInformation)
-{
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
- // Why is the usual if (!callContext.active()... check missing?
-
- UnicodeString inPath = CreateUnicodeString(ObjectAttributes);
-
- RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, inPath);
- unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes =
- makeObjectAttributes(redir, ObjectAttributes);
-
- PRE_REALCALL
- res = ::NtQueryAttributesFile(adjustedAttributes.get(), FileInformation);
- POST_REALCALL
-
- if (redir.redirected) {
- LOG_CALL()
- .addParam("source", ObjectAttributes)
- .addParam("rerouted", adjustedAttributes.get())
- .PARAMWRAP(res);
- }
-
- HOOK_END
-
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtQueryFullAttributesFile(
- POBJECT_ATTRIBUTES ObjectAttributes, PFILE_NETWORK_OPEN_INFORMATION FileInformation)
-{
- PreserveGetLastError ntFunctionsDoNotChangeGetLastError;
-
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START_GROUP(MutExHookGroup::FILE_ATTRIBUTES)
-
- if (!callContext.active()) {
- return ::NtQueryFullAttributesFile(ObjectAttributes, FileInformation);
- }
-
- UnicodeString inPath;
- try {
- inPath = CreateUnicodeString(ObjectAttributes);
- } catch (const std::exception&) {
- return ::NtQueryFullAttributesFile(ObjectAttributes, FileInformation);
- }
-
- RedirectionInfo redir = applyReroute(READ_CONTEXT(), callContext, inPath);
- unique_ptr_deleter<OBJECT_ATTRIBUTES> adjustedAttributes =
- makeObjectAttributes(redir, ObjectAttributes);
-
- PRE_REALCALL
- res = ::NtQueryFullAttributesFile(adjustedAttributes.get(), FileInformation);
- POST_REALCALL
-
- if (redir.redirected) {
- LOG_CALL()
- .addParam("source", ObjectAttributes)
- .addParam("rerouted", adjustedAttributes.get())
- .PARAMWRAP(res);
- }
-
- HOOK_END
-
- return res;
-}
-
-NTSTATUS WINAPI usvfs::hook_NtTerminateProcess(HANDLE ProcessHandle,
- NTSTATUS ExitStatus)
-{
- // this hook is normally called when terminating another process, in which
- // case there's nothing to do
- //
- // if the current process exits normally, the ExitProcess() hook is called
- // and disconnects from the vfs; if the current process crashes, this hook
- // is not called, the process just dies
- //
- // but a process can also terminate itself, bypassing ExitProcess(), and
- // ending up here, in which case the vfs should be disconnected
- //
- // NtTerminateProcess() can be called two different ways to terminate the
- // current process:
- // - with a valid handle for the current process, or
- // - with -1, because that's what GetCurrentProcess() returns
- //
- // it's unclear what a NULL handle represents, the behaviour is not
- // documented anywhere, but looking at ReactOS, it's also interpreted as the
- // current process
-
- NTSTATUS res = STATUS_SUCCESS;
-
- HOOK_START
-
- const bool isCurrentProcess = ProcessHandle == (HANDLE)-1 || ProcessHandle == 0 ||
- GetProcessId(ProcessHandle) == GetCurrentProcessId();
-
- if (isCurrentProcess) {
- usvfsDisconnectVFS();
- }
-
- res = ::NtTerminateProcess(ProcessHandle, ExitStatus);
-
- HOOK_END
-
- return res;
-}
diff --git a/libs/usvfs/src/usvfs_dll/hooks/ntdll.h b/libs/usvfs/src/usvfs_dll/hooks/ntdll.h
deleted file mode 100644
index f3db4a6..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/ntdll.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include <dllimport.h>
-#include <loghelpers.h>
-#include <ntdll_declarations.h>
-
-namespace usvfs
-{
-
-DLLEXPORT NTSTATUS WINAPI
-hook_NtQueryFullAttributesFile(POBJECT_ATTRIBUTES ObjectAttributes,
- PFILE_NETWORK_OPEN_INFORMATION FileInformation);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryAttributesFile(
- POBJECT_ATTRIBUTES ObjectAttributes, PFILE_BASIC_INFORMATION FileInformation);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFile(
- HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
- PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
- FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry,
- PUNICODE_STRING FileName, BOOLEAN RestartScan);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFileEx(
- HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
- PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
- FILE_INFORMATION_CLASS FileInformationClass, ULONG QueryFlags,
- PUNICODE_STRING FileName);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryObject(
- HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass,
- PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationFile(
- HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
- ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationByName(
- POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
- PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock,
- ULONG ShareAccess, ULONG OpenOptions);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtCreateFile(
- PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
- PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
- ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer,
- ULONG EaLength);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtClose(HANDLE Handle);
-
-DLLEXPORT NTSTATUS WINAPI hook_NtTerminateProcess(HANDLE ProcessHandle,
- NTSTATUS ExitStatus);
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/hooks/sharedids.h b/libs/usvfs/src/usvfs_dll/hooks/sharedids.h
deleted file mode 100644
index 9a7531c..0000000
--- a/libs/usvfs/src/usvfs_dll/hooks/sharedids.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include "../hookcontext.h"
-
-typedef std::map<HANDLE, std::wstring> SearchHandleMap;
-
-// maps handles opened for searching to the original search path, which is
-// necessary if the handle creation was rerouted
-DATA_ID(SearchHandles);
diff --git a/libs/usvfs/src/usvfs_dll/maptracker.h b/libs/usvfs/src/usvfs_dll/maptracker.h
deleted file mode 100644
index 6ddf471..0000000
--- a/libs/usvfs/src/usvfs_dll/maptracker.h
+++ /dev/null
@@ -1,691 +0,0 @@
-#pragma once
-
-#include "hookcallcontext.h"
-#include "hookcontext.h"
-#include "stringcast.h"
-
-namespace usvfs
-{
-
-// returns true iff the path exists (checks only real paths)
-static inline bool pathExists(LPCWSTR fileName)
-{
- FunctionGroupLock lock(MutExHookGroup::FILE_ATTRIBUTES);
- DWORD attrib = GetFileAttributesW(fileName);
- return attrib != INVALID_FILE_ATTRIBUTES;
-}
-
-// returns true iff the path exists and is a file (checks only real paths)
-static inline bool pathIsFile(LPCWSTR fileName)
-{
- FunctionGroupLock lock(MutExHookGroup::FILE_ATTRIBUTES);
- DWORD attrib = GetFileAttributesW(fileName);
- return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
-}
-
-// returns true iff the path exists and is a file (checks only real paths)
-static inline bool pathIsDirectory(LPCWSTR fileName)
-{
- FunctionGroupLock lock(MutExHookGroup::FILE_ATTRIBUTES);
- DWORD attrib = GetFileAttributesW(fileName);
- return attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
-}
-
-// returns true iff the path does not exist but it parent directory does (checks only
-// real paths)
-static inline bool pathDirectlyAvailable(LPCWSTR pathName)
-{
- FunctionGroupLock lock(MutExHookGroup::FILE_ATTRIBUTES);
- DWORD attrib = GetFileAttributesW(pathName);
- return attrib == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
-}
-
-class MapTracker
-{
-public:
- std::wstring lookup(const std::wstring& fromPath) const
- {
- if (!fromPath.empty()) {
- std::shared_lock<std::shared_mutex> lock(m_mutex);
- auto find = m_map.find(fromPath);
- if (find != m_map.end())
- return find->second;
- }
- return std::wstring();
- }
-
- bool contains(const std::wstring& fromPath) const
- {
- if (!fromPath.empty()) {
- std::shared_lock<std::shared_mutex> lock(m_mutex);
- auto find = m_map.find(fromPath);
- if (find != m_map.end())
- return true;
- }
- return false;
- }
-
- void insert(const std::wstring& fromPath, const std::wstring& toPath)
- {
- if (fromPath.empty())
- return;
- std::unique_lock<std::shared_mutex> lock(m_mutex);
- m_map[fromPath] = toPath;
- }
-
- bool erase(const std::wstring& fromPath)
- {
- if (fromPath.empty())
- return false;
- std::unique_lock<std::shared_mutex> lock(m_mutex);
- return m_map.erase(fromPath);
- }
-
-private:
- mutable std::shared_mutex m_mutex;
- std::unordered_map<std::wstring, std::wstring> m_map;
-};
-
-extern MapTracker k32DeleteTracker;
-extern MapTracker k32FakeDirTracker;
-
-class RerouteW
-{
- std::wstring m_Buffer{};
- std::wstring m_RealPath{};
- bool m_Rerouted{false};
- LPCWSTR m_FileName{nullptr};
- bool m_PathCreated{false};
- bool m_NewReroute{false};
-
- RedirectionTree::NodePtrT m_FileNode;
-
-public:
- RerouteW() = default;
-
- RerouteW(RerouteW&& reference)
- : m_Buffer(std::move(reference.m_Buffer)),
- m_RealPath(std::move(reference.m_RealPath)), m_Rerouted(reference.m_Rerouted),
- m_PathCreated(reference.m_PathCreated), m_NewReroute(reference.m_NewReroute),
- m_FileNode(std::move(reference.m_FileNode))
- {
- m_FileName = reference.m_FileName != nullptr ? m_Buffer.c_str() : nullptr;
- reference.m_FileName = nullptr;
- }
-
- RerouteW& operator=(RerouteW&& reference)
- {
- m_Buffer = std::move(reference.m_Buffer);
- m_RealPath = std::move(reference.m_RealPath);
- m_Rerouted = reference.m_Rerouted;
- m_PathCreated = reference.m_PathCreated;
- m_NewReroute = reference.m_NewReroute;
- m_FileName = reference.m_FileName != nullptr ? m_Buffer.c_str() : nullptr;
- m_FileNode = std::move(reference.m_FileNode);
- return *this;
- }
-
- RerouteW(const RerouteW& reference) = delete;
- RerouteW& operator=(const RerouteW&) = delete;
-
- LPCWSTR fileName() const { return m_FileName; }
-
- const std::wstring& buffer() const { return m_Buffer; }
-
- bool wasRerouted() const { return m_Rerouted; }
-
- bool newReroute() const { return m_NewReroute; }
-
- void insertMapping(const HookContext::Ptr& context, bool directory = false)
- {
- if (directory) {
- addDirectoryMapping(context, m_RealPath, m_FileName);
-
- // In case we have just created a "fake" directory, it is no longer fake and need
- // to remove it and all its parent folders from the fake map:
- std::wstring dir = m_FileName;
- while (k32FakeDirTracker.erase(dir))
- dir = fs::path(dir).parent_path().wstring();
- } else {
- // if (m_PathCreated)
- // addDirectoryMapping(context, fs::path(m_RealPath).parent_path(),
- // fs::path(m_FileName).parent_path());
-
- spdlog::get("hooks")->info(
- "mapping file in vfs: {}, {}",
- shared::string_cast<std::string>(m_RealPath, shared::CodePage::UTF8),
- shared::string_cast<std::string>(m_FileName, shared::CodePage::UTF8));
- m_FileNode = context->redirectionTable().addFile(
- m_RealPath, RedirectionDataLocal(shared::string_cast<std::string>(
- m_FileName, shared::CodePage::UTF8)));
-
- k32DeleteTracker.erase(m_RealPath);
- }
- }
-
- void removeMapping(const HookContext::ConstPtr& readContext, bool directory = false)
- {
- bool addToDelete = false;
- bool dontAddToDelete = false;
-
- // We need to track deleted files even if they were not rerouted (i.e. files deleted
- // from the real folder which there is a virtualized mapped folder on top of it).
- // Since we don't want to add, *every* file which is deleted we check this:
- bool found = wasRerouted();
- if (!found) {
- FindCreateTarget visitor;
- RedirectionTree::VisitorFunction visitorWrapper =
- [&](const RedirectionTree::NodePtrT& node) {
- visitor(node);
- };
- readContext->redirectionTable()->visitPath(m_RealPath, visitorWrapper);
- if (visitor.target.get())
- found = true;
- }
- if (found)
- addToDelete = true;
-
- if (wasRerouted()) {
- if (m_FileNode.get())
- m_FileNode->removeFromTree();
- else
- spdlog::get("usvfs")->warn("Node not removed: {}",
- shared::string_cast<std::string>(m_FileName));
-
- if (!directory) {
- // check if this file was the last file inside a "fake" directory then remove it
- // and possibly also its fake empty parent folders:
- std::wstring parent = m_FileName;
- while (true) {
- parent = fs::path(parent).parent_path().wstring();
- if (k32FakeDirTracker.contains(parent)) {
- dontAddToDelete = true;
- if (RemoveDirectoryW(parent.c_str())) {
- k32FakeDirTracker.erase(parent);
- spdlog::get("usvfs")->info("removed empty fake directory: {}",
- shared::string_cast<std::string>(parent));
- } else if (GetLastError() != ERROR_DIR_NOT_EMPTY) {
- auto error = GetLastError();
- spdlog::get("usvfs")->warn("removing fake directory failed: {}, error={}",
- shared::string_cast<std::string>(parent),
- error);
- break;
- }
- } else
- break;
- }
- }
- }
- if (addToDelete && !dontAddToDelete) {
- k32DeleteTracker.insert(m_RealPath, m_FileName);
- }
- }
-
- static bool createFakePath(fs::path path, LPSECURITY_ATTRIBUTES securityAttributes)
- {
- // sanity and guaranteed recursion end:
- if (!path.has_relative_path())
- throw shared::windows_error(
- "createFakePath() refusing to create non-existing top level path: " +
- path.string());
-
- DWORD attr = GetFileAttributesW(path.c_str());
- DWORD err = GetLastError();
- if (attr != INVALID_FILE_ATTRIBUTES) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY)
- return false; // if directory already exists all is good
- else
- throw shared::windows_error("createFakePath() called on a file: " +
- path.string());
- }
- if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND)
- throw shared::windows_error(
- "createFakePath() GetFileAttributesW failed on: " + path.string(), err);
-
- if (err != ERROR_FILE_NOT_FOUND) // ERROR_FILE_NOT_FOUND means parent directory
- // already exists
- createFakePath(
- path.parent_path(),
- securityAttributes); // otherwise create parent directory (recursively)
-
- BOOL res = CreateDirectoryW(path.c_str(), securityAttributes);
- if (res)
- k32FakeDirTracker.insert(path.wstring(), std::wstring());
- else {
- err = GetLastError();
- throw shared::windows_error(
- "createFakePath() CreateDirectoryW failed on: " + path.string(), err);
- }
- return true;
- }
-
- static bool addDirectoryMapping(const HookContext::Ptr& context,
- const fs::path& originalPath,
- const fs::path& reroutedPath)
- {
- if (originalPath.empty() || reroutedPath.empty()) {
- spdlog::get("hooks")->error("RerouteW::addDirectoryMapping failed: {}, {}",
- shared::string_cast<std::string>(
- originalPath.wstring(), shared::CodePage::UTF8)
- .c_str(),
- shared::string_cast<std::string>(
- reroutedPath.wstring(), shared::CodePage::UTF8)
- .c_str());
- return false;
- }
-
- auto lookupParent =
- context->redirectionTable()->findNode(originalPath.parent_path());
- if (!lookupParent.get() || lookupParent->data().linkTarget.empty()) {
- if (!addDirectoryMapping(context, originalPath.parent_path(),
- reroutedPath.parent_path())) {
- spdlog::get("hooks")->error("RerouteW::addDirectoryMapping failed: {}, {}",
- shared::string_cast<std::string>(
- originalPath.wstring(), shared::CodePage::UTF8)
- .c_str(),
- shared::string_cast<std::string>(
- reroutedPath.wstring(), shared::CodePage::UTF8)
- .c_str());
- return false;
- }
- }
-
- std::string reroutedU8 = shared::string_cast<std::string>(reroutedPath.wstring(),
- shared::CodePage::UTF8);
- if (reroutedU8.empty() || reroutedU8[reroutedU8.size() - 1] != '\\')
- reroutedU8 += "\\";
-
- spdlog::get("hooks")->info("mapping directory in vfs: {}, {}",
- shared::string_cast<std::string>(originalPath.wstring(),
- shared::CodePage::UTF8),
- reroutedU8.c_str());
-
- context->redirectionTable().addDirectory(
- originalPath, RedirectionDataLocal(reroutedU8),
- shared::FLAG_DIRECTORY | shared::FLAG_CREATETARGET);
-
- fs::directory_iterator end_itr;
-
- // cycle through the directory
- for (fs::directory_iterator itr(reroutedPath); itr != end_itr; ++itr) {
- // If it's not a directory, add it to the VFS, if it is recurse into it
- if (is_regular_file(itr->path())) {
- std::string fileReroutedU8 = shared::string_cast<std::string>(
- itr->path().wstring(), shared::CodePage::UTF8);
- spdlog::get("hooks")->info(
- "mapping file in vfs: {}, {}",
- shared::string_cast<std::string>(
- (originalPath / itr->path().filename()).wstring(),
- shared::CodePage::UTF8),
- fileReroutedU8.c_str());
- context->redirectionTable().addFile(
- fs::path(originalPath / itr->path().filename()),
- RedirectionDataLocal(fileReroutedU8));
- } else {
- addDirectoryMapping(context, originalPath / itr->path().filename(),
- reroutedPath / itr->path().filename());
- }
- }
-
- return true;
- }
-
- template <class char_t>
- static bool interestingPathImpl(const char_t* inPath)
- {
- if (!inPath || !inPath[0])
- return false;
- // ignore \\.\ unless its a \\.\?:
- if (inPath[0] == '\\' && inPath[1] == '\\' && inPath[2] == '.' &&
- inPath[3] == '\\' && (!inPath[4] || inPath[5] != ':'))
- return false;
- // ignore L"hid#":
- if ((inPath[0] == 'h' || inPath[0] == 'H') &&
- ((inPath[1] == 'i' || inPath[1] == 'I')) &&
- ((inPath[2] == 'd' || inPath[2] == 'D')) && inPath[3] == '#')
- return false;
- return true;
- }
-
- static bool interestingPath(const char* inPath)
- {
- return interestingPathImpl(inPath);
- }
- static bool interestingPath(const wchar_t* inPath)
- {
- return interestingPathImpl(inPath);
- }
-
- static fs::path absolutePath(const wchar_t* inPath)
- {
- if (shared::startswith(inPath, LR"(\\?\)") ||
- shared::startswith(inPath, LR"(\??\)")) {
- inPath += 4;
- return inPath;
- } else if ((shared::startswith(inPath, LR"(\\localhost\)") ||
- shared::startswith(inPath, LR"(\\127.0.0.1\)")) &&
- inPath[13] == L'$') {
- std::wstring newPath;
- newPath += towupper(inPath[12]);
- newPath += L':';
- newPath += &inPath[14];
- return newPath;
- } else if (inPath[0] == L'\0' || inPath[1] == L':') {
- return inPath;
- } else if (inPath[0] == L'\\' || inPath[0] == L'/') {
- return fs::path(winapi::wide::getFullPathName(inPath).first);
- }
- WCHAR currentDirectory[MAX_PATH];
- ::GetCurrentDirectoryW(MAX_PATH, currentDirectory);
- fs::path finalPath = fs::path(currentDirectory) / inPath;
- return finalPath;
- // winapi::wide::getFullPathName(inPath).first;
- }
-
- static fs::path canonizePath(const fs::path& inPath)
- {
- fs::path p = inPath.lexically_normal();
- if (p.filename_is_dot())
- p = p.remove_filename();
- return p.make_preferred();
- }
-
- static RerouteW create(const HookContext::ConstPtr& context,
- const HookCallContext& callContext, const wchar_t* inPath,
- bool inverse = false)
- {
- RerouteW result;
-
- if (interestingPath(inPath) && callContext.active()) {
- const auto& lookupPath = canonizePath(absolutePath(inPath));
- result.m_RealPath = lookupPath.wstring();
-
- result.m_Buffer = k32DeleteTracker.lookup(result.m_RealPath);
- bool found = !result.m_Buffer.empty();
- if (found) {
- spdlog::get("hooks")->info(
- "Rerouting file open to location of deleted file: {}",
- shared::string_cast<std::string>(result.m_Buffer));
- result.m_NewReroute = true;
- } else {
- const RedirectionTreeContainer& table =
- inverse ? context->inverseTable() : context->redirectionTable();
- result.m_FileNode = table->findNode(lookupPath);
-
- if (result.m_FileNode.get() && (!result.m_FileNode->data().linkTarget.empty() ||
- result.m_FileNode->isDirectory())) {
- if (!result.m_FileNode->data().linkTarget.empty()) {
- result.m_Buffer = shared::string_cast<std::wstring>(
- result.m_FileNode->data().linkTarget.c_str(), shared::CodePage::UTF8);
- } else {
- result.m_Buffer = result.m_FileNode->path().wstring();
- }
- found = true;
- }
- }
- if (found) {
- result.m_Rerouted = true;
-
- wchar_t inIt = inPath[wcslen(inPath) - 1];
- std::wstring::iterator outIt = result.m_Buffer.end() - 1;
- if ((*outIt == L'\\' || *outIt == L'/') && !(inIt == L'\\' || inIt == L'/'))
- result.m_Buffer.erase(outIt);
- std::replace(result.m_Buffer.begin(), result.m_Buffer.end(), L'/', L'\\');
- } else
- result.m_Buffer = inPath;
- } else if (inPath)
- result.m_Buffer = inPath;
-
- if (inPath)
- result.m_FileName = result.m_Buffer.c_str();
- return result;
- }
-
- static RerouteW createNew(const HookContext::ConstPtr& context,
- const HookCallContext& callContext, LPCWSTR inPath,
- bool createPath = true,
- LPSECURITY_ATTRIBUTES securityAttributes = nullptr)
- {
- RerouteW result;
-
- if (interestingPath(inPath) && callContext.active()) {
- const auto& lookupPath = canonizePath(absolutePath(inPath));
- result.m_RealPath = lookupPath.wstring();
-
- result.m_Buffer = k32DeleteTracker.lookup(result.m_RealPath);
- bool found = !result.m_Buffer.empty();
- if (found)
- spdlog::get("hooks")->info(
- "Rerouting file creation to original location of deleted file: {}",
- shared::string_cast<std::string>(result.m_Buffer));
- else {
- FindCreateTarget visitor;
- RedirectionTree::VisitorFunction visitorWrapper =
- [&](const RedirectionTree::NodePtrT& node) {
- visitor(node);
- };
- context->redirectionTable()->visitPath(lookupPath, visitorWrapper);
- if (visitor.target.get()) {
- // the visitor has found the last (deepest in the directory hierarchy)
- // create-target
- fs::path relativePath =
- shared::make_relative(visitor.target->path(), lookupPath);
- result.m_Buffer =
- (fs::path(visitor.target->data().linkTarget.c_str()) / relativePath)
- .wstring();
- found = true;
- }
- }
-
- if (found) {
- if (createPath) {
- try {
- FunctionGroupLock lock(MutExHookGroup::ALL_GROUPS);
- result.m_PathCreated = createFakePath(
- fs::path(result.m_Buffer).parent_path(), securityAttributes);
- } catch (const std::exception& e) {
- spdlog::get("hooks")->error(
- "failed to create {}: {}",
- shared::string_cast<std::string>(result.m_Buffer), e.what());
- }
- }
-
- wchar_t inIt = inPath[wcslen(inPath) - 1];
- std::wstring::iterator outIt = result.m_Buffer.end() - 1;
- if ((*outIt == L'\\' || *outIt == L'/') && !(inIt == L'\\' || inIt == L'/'))
- result.m_Buffer.erase(outIt);
- std::replace(result.m_Buffer.begin(), result.m_Buffer.end(), L'/', L'\\');
- result.m_Rerouted = true;
- result.m_NewReroute = true;
- } else
- result.m_Buffer = inPath;
- } else if (inPath)
- result.m_Buffer = inPath;
-
- if (inPath)
- result.m_FileName = result.m_Buffer.c_str();
- return result;
- }
-
- static RerouteW createOrNew(const HookContext::ConstPtr& context,
- const HookCallContext& callContext, LPCWSTR inPath,
- bool createPath = true,
- LPSECURITY_ATTRIBUTES securityAttributes = nullptr)
- {
- {
- auto res = create(context, callContext, inPath);
- if (res.wasRerouted() || !interestingPath(inPath) || !callContext.active() ||
- pathExists(inPath))
- return std::move(res);
- }
- return createNew(context, callContext, inPath, createPath, securityAttributes);
- }
-
- static RerouteW noReroute(LPCWSTR inPath)
- {
- RerouteW result;
- if (inPath)
- result.m_Buffer = inPath;
- if (inPath && inPath[0] && !shared::startswith(inPath, L"hid#"))
- std::replace(result.m_Buffer.begin(), result.m_Buffer.end(), L'/', L'\\');
- result.m_FileName = result.m_Buffer.c_str();
- return result;
- }
-
-private:
- struct FindCreateTarget
- {
- RedirectionTree::NodePtrT target;
- void operator()(RedirectionTree::NodePtrT node)
- {
- if (node->hasFlag(shared::FLAG_CREATETARGET)) {
- target = node;
- }
- }
- };
-};
-
-class CreateRerouter
-{
-public:
- bool rerouteCreate(const HookContext::ConstPtr& context,
- const HookCallContext& callContext, LPCWSTR lpFileName,
- DWORD& dwCreationDisposition, DWORD dwDesiredAccess,
- LPSECURITY_ATTRIBUTES lpSecurityAttributes)
- {
- enum class Open
- {
- existing,
- create,
- empty
- };
- Open open = Open::existing;
-
- // Notice since we are calling our patched GetFileAttributesW here this will also
- // check virtualized paths
- DWORD virtAttr = GetFileAttributesW(lpFileName);
- bool isFile = virtAttr != INVALID_FILE_ATTRIBUTES &&
- (virtAttr & FILE_ATTRIBUTE_DIRECTORY) == 0;
- m_isDir =
- virtAttr != INVALID_FILE_ATTRIBUTES && (virtAttr & FILE_ATTRIBUTE_DIRECTORY);
-
- switch (dwCreationDisposition) {
- case CREATE_ALWAYS:
- open = Open::create;
- if (isFile || m_isDir) {
- m_error = ERROR_ALREADY_EXISTS;
- }
- break;
-
- case CREATE_NEW:
- if (isFile || m_isDir) {
- m_error = ERROR_FILE_EXISTS;
- return false;
- } else {
- open = Open::create;
- }
- break;
-
- case OPEN_ALWAYS:
- if (isFile || m_isDir) {
- m_error = ERROR_ALREADY_EXISTS;
- } else {
- open = Open::create;
- }
- break;
-
- case TRUNCATE_EXISTING:
- if ((dwDesiredAccess & GENERIC_WRITE) == 0) {
- m_error = ERROR_INVALID_PARAMETER;
- return false;
- }
- if (isFile || m_isDir)
- open = Open::empty;
- break;
- }
-
- if (m_isDir && pathIsDirectory(lpFileName))
- m_reroute = RerouteW::noReroute(lpFileName);
- else
- m_reroute = RerouteW::create(context, callContext, lpFileName);
-
- if (m_reroute.wasRerouted() && open == Open::create &&
- pathIsDirectory(m_reroute.fileName()))
- m_reroute = RerouteW::createNew(context, callContext, lpFileName, true,
- lpSecurityAttributes);
-
- if (!m_isDir && !isFile && !m_reroute.wasRerouted() &&
- (open == Open::create || open == Open::empty)) {
- m_reroute = RerouteW::createNew(context, callContext, lpFileName, true,
- lpSecurityAttributes);
-
- bool newFile =
- !m_reroute.wasRerouted() && pathDirectlyAvailable(m_reroute.fileName());
- if (newFile && open == Open::empty)
- // TRUNCATE_EXISTING will fail since the new file doesn't exist, so change
- // disposition:
- dwCreationDisposition = CREATE_ALWAYS;
- }
-
- return true;
- }
-
- // rerouteNew is used for rerouting the destination of copy/move operations. Assumes
- // that the call will be skipped if false is returned.
- bool rerouteNew(const HookContext::ConstPtr& context, HookCallContext& callContext,
- LPCWSTR lpFileName, bool replaceExisting, const char* hookName)
- {
- DWORD disposition = replaceExisting ? CREATE_ALWAYS : CREATE_NEW;
- if (!rerouteCreate(context, callContext, lpFileName, disposition, GENERIC_WRITE,
- nullptr)) {
- spdlog::get("hooks")->info(
- "{} guaranteed failure, skipping original call: {}, replaceExisting={}, "
- "error={}",
- hookName,
- shared::string_cast<std::string>(lpFileName, shared::CodePage::UTF8),
- replaceExisting ? "true" : "false", error());
-
- callContext.updateLastError(error());
- return false;
- }
- return true;
- }
-
- void updateResult(HookCallContext& callContext, bool success)
- {
- m_originalError = callContext.lastError();
- if (success) {
- // m_error != ERROR_SUCCESS means we are overriding the error on success
- if (m_error == ERROR_SUCCESS)
- m_error = m_originalError;
- } else if (m_originalError == ERROR_PATH_NOT_FOUND && m_directlyAvailable)
- m_error = ERROR_FILE_NOT_FOUND;
- else
- m_error = m_originalError;
- if (m_error != m_originalError)
- callContext.updateLastError(m_error);
- }
-
- DWORD error() const { return m_error; }
- DWORD originalError() const { return m_originalError; }
- bool changedError() const { return m_error != m_originalError; }
-
- bool isDir() const { return m_isDir; }
- bool newReroute() const { return m_reroute.newReroute(); }
- bool wasRerouted() const { return m_reroute.wasRerouted(); }
- LPCWSTR fileName() const { return m_reroute.fileName(); }
-
- void insertMapping(const HookContext::Ptr& context, bool directory = false)
- {
- m_reroute.insertMapping(context, directory);
- }
-
-private:
- DWORD m_error = ERROR_SUCCESS;
- DWORD m_originalError = ERROR_SUCCESS;
- bool m_directlyAvailable = false;
- bool m_isDir = false;
- RerouteW m_reroute;
-};
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/pch.cpp b/libs/usvfs/src/usvfs_dll/pch.cpp
deleted file mode 100644
index 1d9f38c..0000000
--- a/libs/usvfs/src/usvfs_dll/pch.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "pch.h"
diff --git a/libs/usvfs/src/usvfs_dll/redirectiontree.cpp b/libs/usvfs/src/usvfs_dll/redirectiontree.cpp
deleted file mode 100644
index e521365..0000000
--- a/libs/usvfs/src/usvfs_dll/redirectiontree.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-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 "redirectiontree.h"
-
-std::ostream& usvfs::operator<<(std::ostream& stream, const RedirectionData& data)
-{
- stream << data.linkTarget;
- return stream;
-}
diff --git a/libs/usvfs/src/usvfs_dll/redirectiontree.h b/libs/usvfs/src/usvfs_dll/redirectiontree.h
deleted file mode 100644
index 6a2238a..0000000
--- a/libs/usvfs/src/usvfs_dll/redirectiontree.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-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/>.
-*/
-#pragma once
-
-#include <directory_tree.h>
-
-namespace usvfs
-{
-
-// typedef boost::interprocess::basic_string<char, std::char_traits<char>,
-// shared::CharAllocatorT> StringT;
-
-namespace shared
-{
- static const TreeFlags FLAG_CREATETARGET = FLAG_FIRSTUSERFLAG + 0x00;
-}
-
-struct RedirectionDataLocal
-{
-
- RedirectionDataLocal(const char* target) : linkTarget(target) {}
-
- RedirectionDataLocal(const std::string& target) : linkTarget(target) {}
-
- std::string linkTarget;
-};
-
-struct RedirectionData
-{
-
- RedirectionData(const RedirectionData& reference,
- const shared::VoidAllocatorT& allocator)
- : linkTarget(reference.linkTarget.c_str(), allocator)
- {}
-
- RedirectionData(const RedirectionDataLocal& reference,
- const shared::VoidAllocatorT& allocator)
- : linkTarget(reference.linkTarget.c_str(), allocator)
- {}
-
- RedirectionData(const char* target, const shared::VoidAllocatorT& allocator)
- : linkTarget(target, allocator)
- {}
-
- shared::StringT linkTarget;
-};
-
-std::ostream& operator<<(std::ostream& stream, const RedirectionData& data);
-
-template <>
-inline void shared::dataAssign<RedirectionData>(RedirectionData& destination,
- const RedirectionData& source)
-{
- destination.linkTarget.assign(source.linkTarget.c_str());
-}
-
-template <>
-inline RedirectionData
-shared::createDataEmpty<RedirectionData>(const VoidAllocatorT& allocator)
-{
- return RedirectionData("", allocator);
-}
-
-template <typename T>
-struct shared::SHMDataCreator<RedirectionData, T>
-{
- static RedirectionData create(T source, const VoidAllocatorT& allocator)
- {
- return RedirectionData(source, allocator);
- }
-};
-
-template <>
-struct shared::SHMDataCreator<RedirectionData, RedirectionData>
-{
- static RedirectionData create(const RedirectionData& source,
- const VoidAllocatorT& allocator)
- {
- return RedirectionData(source, allocator);
- }
-};
-
-using RedirectionTree = shared::DirectoryTree<RedirectionData>;
-using RedirectionTreeContainer = shared::TreeContainer<RedirectionTree>;
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/semaphore.cpp b/libs/usvfs/src/usvfs_dll/semaphore.cpp
deleted file mode 100644
index 609b2f5..0000000
--- a/libs/usvfs/src/usvfs_dll/semaphore.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "semaphore.h"
-#include "exceptionex.h"
-
-RecursiveBenaphore::RecursiveBenaphore() : m_Counter(0), m_OwnerId(0UL), m_Recursion(0)
-{
- m_Semaphore = ::CreateSemaphore(nullptr, 1, 1, nullptr);
-}
-
-RecursiveBenaphore::~RecursiveBenaphore()
-{
- ::CloseHandle(m_Semaphore);
-}
-
-void RecursiveBenaphore::wait(DWORD timeout)
-{
- DWORD tid = ::GetCurrentThreadId();
-
- if (::_InterlockedIncrement(&m_Counter) > 1) {
- if (tid != m_OwnerId) {
- int tries = 3;
- while (::WaitForSingleObject(m_Semaphore, timeout) != WAIT_OBJECT_0) {
- HANDLE owner = ::OpenThread(SYNCHRONIZE, FALSE, m_OwnerId);
- ON_BLOCK_EXIT([owner]() {
- ::CloseHandle(owner);
- });
- if ((tries <= 0) || (::WaitForSingleObject(owner, 0) == WAIT_OBJECT_0)) {
- // owner has quit without releasing the semaphore!
- m_Recursion = 0;
- spdlog::get("usvfs")->error("thread {} never released the mutex", m_OwnerId);
- break;
- } else {
- --tries;
- }
- }
- }
- }
- m_OwnerId = tid;
- ++m_Recursion;
-}
-
-void RecursiveBenaphore::signal()
-{
- if (m_Recursion == 0) {
- return;
- }
- // no validation the signaling thread is the one owning the lock
- DWORD recursion = --m_Recursion;
- if (recursion == 0) {
- m_OwnerId = 0;
- }
- DWORD result = ::_InterlockedDecrement(&m_Counter);
- if (result > 0) {
- if (recursion == 0) {
- ::ReleaseSemaphore(m_Semaphore, 1, nullptr);
- }
- }
-}
diff --git a/libs/usvfs/src/usvfs_dll/semaphore.h b/libs/usvfs/src/usvfs_dll/semaphore.h
deleted file mode 100644
index 1048c47..0000000
--- a/libs/usvfs/src/usvfs_dll/semaphore.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-// based on code by Jeff Preshing
-
-// this is a synchronization class that prefers
-// undefined behaviour over deadlock. It's utterly broken
-// and needs to be replaced in time.
-
-class RecursiveBenaphore
-{
-
-public:
- RecursiveBenaphore();
- ~RecursiveBenaphore();
-
- // wait on the semaphore. after timeout this will check if the current owner
- // thread is still alive and steal the semaphore if it isn't. Otherwise this
- // will continue to wait.
- void wait(DWORD timeout = INFINITE);
- void signal();
-
-private:
- LONG m_Counter;
- DWORD m_OwnerId;
- int m_Recursion;
- HANDLE m_Semaphore;
-};
diff --git a/libs/usvfs/src/usvfs_dll/sharedparameters.cpp b/libs/usvfs/src/usvfs_dll/sharedparameters.cpp
deleted file mode 100644
index 95cd0b2..0000000
--- a/libs/usvfs/src/usvfs_dll/sharedparameters.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-#include <logging.h>
-#include <sharedparameters.h>
-#include <usvfsparametersprivate.h>
-
-namespace usvfs
-{
-
-ForcedLibrary::ForcedLibrary(const std::string& process, const std::string& path,
- const shared::VoidAllocatorT& alloc)
- : m_processName(process.begin(), process.end(), alloc),
- m_libraryPath(path.begin(), path.end(), alloc)
-{}
-
-std::string ForcedLibrary::processName() const
-{
- return {m_processName.begin(), m_processName.end()};
-}
-
-std::string ForcedLibrary::libraryPath() const
-{
- return {m_libraryPath.begin(), m_libraryPath.end()};
-}
-
-SharedParameters::SharedParameters(const usvfsParameters& reference,
- const shared::VoidAllocatorT& allocator)
- : m_instanceName(reference.instanceName, allocator),
- m_currentSHMName(reference.currentSHMName, allocator),
- m_currentInverseSHMName(reference.currentInverseSHMName, allocator),
- m_debugMode(reference.debugMode), m_logLevel(reference.logLevel),
- m_crashDumpsType(reference.crashDumpsType),
- m_crashDumpsPath(reference.crashDumpsPath, allocator),
- m_delayProcess(reference.delayProcessMs), m_userCount(1),
- m_processBlacklist(allocator), m_processList(allocator),
- m_fileSuffixSkipList(allocator), m_directorySkipList(allocator),
- m_forcedLibraries(allocator)
-{}
-
-usvfsParameters SharedParameters::makeLocal() const
-{
- bi::scoped_lock lock(m_mutex);
-
- return usvfsParameters(m_instanceName.c_str(), m_currentSHMName.c_str(),
- m_currentInverseSHMName.c_str(), m_debugMode, m_logLevel,
- m_crashDumpsType, m_crashDumpsPath.c_str(),
- m_delayProcess.count());
-}
-
-std::string SharedParameters::instanceName() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_instanceName.begin(), m_instanceName.end()};
-}
-
-std::string SharedParameters::currentSHMName() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_currentSHMName.begin(), m_currentSHMName.end()};
-}
-
-std::string SharedParameters::currentInverseSHMName() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_currentInverseSHMName.begin(), m_currentInverseSHMName.end()};
-}
-
-void SharedParameters::setSHMNames(const std::string& current,
- const std::string& inverse)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_currentSHMName.assign(current.begin(), current.end());
- m_currentInverseSHMName.assign(inverse.begin(), inverse.end());
-}
-
-void SharedParameters::setDebugParameters(LogLevel level, CrashDumpsType dumpType,
- const std::string& dumpPath,
- std::chrono::milliseconds delayProcess)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_logLevel = level;
- m_crashDumpsType = dumpType;
- m_crashDumpsPath.assign(dumpPath.begin(), dumpPath.end());
- m_delayProcess = delayProcess;
-}
-
-std::size_t SharedParameters::userConnected()
-{
- bi::scoped_lock lock(m_mutex);
- return ++m_userCount;
-}
-
-std::size_t SharedParameters::userDisconnected()
-{
- bi::scoped_lock lock(m_mutex);
- return --m_userCount;
-}
-
-std::size_t SharedParameters::userCount()
-{
- bi::scoped_lock lock(m_mutex);
- return m_userCount;
-}
-
-std::size_t SharedParameters::registeredProcessCount() const
-{
- bi::scoped_lock lock(m_mutex);
- return m_processList.size();
-}
-
-std::vector<DWORD> SharedParameters::registeredProcesses() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_processList.begin(), m_processList.end()};
-}
-
-void SharedParameters::registerProcess(DWORD pid)
-{
- bi::scoped_lock lock(m_mutex);
- m_processList.insert(pid);
-}
-
-void SharedParameters::unregisterProcess(DWORD pid)
-{
- {
- bi::scoped_lock lock(m_mutex);
-
- auto itor = m_processList.find(pid);
-
- if (itor != m_processList.end()) {
- m_processList.erase(itor);
- return;
- }
- }
-
- spdlog::get("usvfs")->error("cannot unregister process {}, not in list", pid);
-}
-
-void SharedParameters::blacklistExecutable(const std::string& name)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_processBlacklist.insert(
- shared::StringT(name.begin(), name.end(), m_processBlacklist.get_allocator()));
-}
-
-void SharedParameters::clearExecutableBlacklist()
-{
- bi::scoped_lock lock(m_mutex);
- m_processBlacklist.clear();
-}
-
-bool SharedParameters::executableBlacklisted(const std::string& appName,
- const std::string& cmdLine) const
-{
- bool blacklisted = false;
- std::string log;
-
- {
- bi::scoped_lock lock(m_mutex);
-
- for (const shared::StringT& sitem : m_processBlacklist) {
- const auto item = "\\" + std::string(sitem.begin(), sitem.end());
-
- if (!appName.empty()) {
- if (boost::algorithm::iends_with(appName, item)) {
- blacklisted = true;
- log = std::format("application {} is blacklisted", appName);
- break;
- }
- }
-
- if (!cmdLine.empty()) {
- if (boost::algorithm::icontains(cmdLine, item)) {
- blacklisted = true;
- log = std::format("command line {} is blacklisted", cmdLine);
- break;
- }
- }
- }
- }
-
- if (blacklisted) {
- spdlog::get("usvfs")->info(log);
- return true;
- }
-
- return false;
-}
-
-void SharedParameters::addSkipFileSuffix(const std::string& fileSuffix)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_fileSuffixSkipList.insert(shared::StringT(fileSuffix.begin(), fileSuffix.end(),
- m_fileSuffixSkipList.get_allocator()));
-}
-
-void SharedParameters::clearSkipFileSuffixes()
-{
- bi::scoped_lock lock(m_mutex);
- m_fileSuffixSkipList.clear();
-}
-
-std::vector<std::string> SharedParameters::skipFileSuffixes() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_fileSuffixSkipList.begin(), m_fileSuffixSkipList.end()};
-}
-
-void SharedParameters::addSkipDirectory(const std::string& directory)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_directorySkipList.insert(shared::StringT(directory.begin(), directory.end(),
- m_directorySkipList.get_allocator()));
-}
-
-void SharedParameters::clearSkipDirectories()
-{
- bi::scoped_lock lock(m_mutex);
- m_directorySkipList.clear();
-}
-
-std::vector<std::string> SharedParameters::skipDirectories() const
-{
- bi::scoped_lock lock(m_mutex);
- return {m_directorySkipList.begin(), m_directorySkipList.end()};
-}
-
-void SharedParameters::addForcedLibrary(const std::string& processName,
- const std::string& libraryPath)
-{
- bi::scoped_lock lock(m_mutex);
-
- m_forcedLibraries.push_front(
- ForcedLibrary(processName, libraryPath, m_forcedLibraries.get_allocator()));
-}
-
-std::vector<std::string>
-SharedParameters::forcedLibraries(const std::string& processName)
-{
- std::vector<std::string> v;
-
- {
- bi::scoped_lock lock(m_mutex);
-
- for (const auto& lib : m_forcedLibraries) {
- if (boost::algorithm::iequals(processName, lib.processName())) {
- v.push_back(lib.libraryPath());
- }
- }
- }
-
- return v;
-}
-
-void SharedParameters::clearForcedLibraries()
-{
- bi::scoped_lock lock(m_mutex);
- m_forcedLibraries.clear();
-}
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/stringcast_boost.h b/libs/usvfs/src/usvfs_dll/stringcast_boost.h
deleted file mode 100644
index d2544ec..0000000
--- a/libs/usvfs/src/usvfs_dll/stringcast_boost.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-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 <stringcast.h>
-
-namespace usvfs
-{
-namespace shared
-{
-
- template <typename ToT, typename CharT, typename Traits, typename Allocator>
- class string_cast_impl<ToT, boost::container::basic_string<CharT, Traits, Allocator>>
- {
- public:
- static ToT
- cast(const boost::container::basic_string<CharT, Traits, Allocator>& source,
- CodePage codePage, size_t sourceLength)
- {
- return string_cast_impl<ToT, const CharT*>::cast(source.c_str(), codePage,
- sourceLength);
- }
- };
-
-} // namespace shared
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_dll/usvfs.cpp b/libs/usvfs/src/usvfs_dll/usvfs.cpp
deleted file mode 100644
index 56d9f71..0000000
--- a/libs/usvfs/src/usvfs_dll/usvfs.cpp
+++ /dev/null
@@ -1,965 +0,0 @@
-/*
-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 "usvfs.h"
-#include "hookmanager.h"
-#include "loghelpers.h"
-#include "redirectiontree.h"
-#include "usvfs_version.h"
-#include "usvfsparametersprivate.h"
-#include <inject.h>
-#include <shmlogger.h>
-#include <spdlog/sinks/null_sink.h>
-#include <spdlog/sinks/stdout_sinks.h>
-#include <stringcast.h>
-#include <ttrampolinepool.h>
-#include <winapi.h>
-
-// note that there's a mix of boost and std filesystem stuff in this file and
-// that they're not completely compatible
-#include <filesystem>
-
-namespace bfs = boost::filesystem;
-namespace ush = usvfs::shared;
-namespace bip = boost::interprocess;
-namespace ba = boost::algorithm;
-
-using usvfs::log::ConvertLogLevel;
-
-usvfs::HookManager* manager = nullptr;
-usvfs::HookContext* context = nullptr;
-HMODULE dllModule = nullptr;
-PVOID exceptionHandler = nullptr;
-CrashDumpsType usvfs_dump_type = CrashDumpsType::None;
-std::wstring usvfs_dump_path;
-
-// this is called for every single file, so it's a bit long winded, but it's
-// as fast as it gets, probably
-//
-template <std::size_t LongestExtension, std::size_t ExtensionsCount>
-bool extensionMatchesCI(
- std::string_view name,
- const std::array<std::string_view, ExtensionsCount>& extensionsLC,
- const std::array<std::string_view, ExtensionsCount>& extensionsUC)
-{
- constexpr std::size_t longestExtensionWithDot = LongestExtension + 1;
-
- // quick check
- if (name.size() < longestExtensionWithDot) {
- return false;
- }
-
- // for each extension
- for (std::size_t i = 0; i < ExtensionsCount; ++i) {
- const std::size_t extensionLength = extensionsLC[i].size();
- const std::size_t extensionLengthWithDot = extensionLength + 1;
-
- // check size
- if (name.size() < extensionLengthWithDot) {
- continue;
- }
-
- // check dot
- if (name[name.size() - extensionLengthWithDot] != '.') {
- continue;
- }
-
- // starts at one past the dot
- const auto* p = name.data() + name.size() - extensionLength;
-
- // set to false as soon as a character doesn't match
- bool found = true;
-
- // for each character in extension
- for (std::size_t c = 0; c < extensionLength; ++c) {
- // checking both lowercase and uppercase
- if (*p != extensionsLC[i][c] && *p != extensionsUC[i][c]) {
- // neither
- found = false;
- break;
- }
-
- // matches, check next
- ++p;
- }
-
- if (found) {
- return true;
- }
- }
-
- return false;
-}
-
-bool shouldAddToInverseTree(std::string_view name)
-{
- static std::array<std::string_view, 3> extensionsLC{"exe", "dll"};
- static std::array<std::string_view, 3> extensionsUC{"EXE", "DLL"};
-
- // must be changed if any extension longer than 3 letters is added
- constexpr std::size_t longestExtension = 3;
-
- return extensionMatchesCI<longestExtension>(name, extensionsLC, extensionsUC);
-}
-
-//
-// Logging
-//
-
-void InitLoggingInternal(bool toConsole, bool connectExistingSHM)
-{
- try {
- if (!toConsole && !SHMLogger::isInstantiated()) {
- if (connectExistingSHM) {
- SHMLogger::open("usvfs");
- } else {
- SHMLogger::create("usvfs");
- }
- }
-
- // a temporary logger was created in DllMain
- spdlog::drop("usvfs");
-#pragma message("need a customized name for the shm")
- auto logger = spdlog::get("usvfs");
- if (logger.get() == nullptr) {
- logger = toConsole ? spdlog::create<spdlog::sinks::stdout_sink_mt>("usvfs")
- : spdlog::create<usvfs::sinks::shm_sink>("usvfs", "usvfs");
- logger->set_pattern("%H:%M:%S.%e [%L] %v");
- }
- logger->set_level(spdlog::level::debug);
-
- spdlog::drop("hooks");
- logger = spdlog::get("hooks");
- if (logger.get() == nullptr) {
- logger = toConsole ? spdlog::create<spdlog::sinks::stdout_sink_mt>("hooks")
- : spdlog::create<usvfs::sinks::shm_sink>("hooks", "usvfs");
- logger->set_pattern("%H:%M:%S.%e <%P:%t> [%L] %v");
- }
- logger->set_level(spdlog::level::debug);
- } catch (const std::exception&) {
- // TODO should really report this
- // OutputDebugStringA((boost::format("init exception: %1%\n") %
- // e.what()).str().c_str());
- if (spdlog::get("usvfs").get() == nullptr) {
- spdlog::create<spdlog::sinks::null_sink_mt>("usvfs");
- }
- if (spdlog::get("hooks").get() == nullptr) {
- spdlog::create<spdlog::sinks::null_sink_mt>("hooks");
- }
- }
-
- spdlog::get("usvfs")->info("usvfs dll {} initialized in process {}",
- USVFS_VERSION_STRING, GetCurrentProcessId());
-}
-
-void WINAPI usvfsInitLogging(bool toConsole)
-{
- InitLoggingInternal(toConsole, false);
-}
-
-extern "C" DLLEXPORT bool WINAPI usvfsGetLogMessages(LPSTR buffer, size_t size,
- bool blocking)
-{
- buffer[0] = '\0';
- try {
- if (blocking) {
- SHMLogger::instance().get(buffer, size);
- return true;
- } else {
- return SHMLogger::instance().tryGet(buffer, size);
- }
- } catch (const std::exception& e) {
- _snprintf_s(buffer, size, _TRUNCATE, "Failed to retrieve log messages: %s",
- e.what());
- return false;
- }
-}
-
-void SetLogLevel(LogLevel level)
-{
- spdlog::get("usvfs")->set_level(ConvertLogLevel(level));
- spdlog::get("hooks")->set_level(ConvertLogLevel(level));
-}
-
-void WINAPI usvfsUpdateParameters(usvfsParameters* p)
-{
- spdlog::get("usvfs")->info("updating parameters:\n"
- " . debugMode: {}\n"
- " . log level: {}\n"
- " . dump type: {}\n"
- " . dump path: {}\n"
- " . delay process: {}ms",
- p->debugMode, usvfsLogLevelToString(p->logLevel),
- usvfsCrashDumpTypeToString(p->crashDumpsType),
- p->crashDumpsPath, p->delayProcessMs);
-
- // update actual values used:
- usvfs_dump_type = p->crashDumpsType;
- usvfs_dump_path =
- ush::string_cast<std::wstring>(p->crashDumpsPath, ush::CodePage::UTF8);
- SetLogLevel(p->logLevel);
-
- // update parameters in context so spawned process will inherit changes:
- context->setDebugParameters(p->logLevel, p->crashDumpsType, p->crashDumpsPath,
- std::chrono::milliseconds(p->delayProcessMs));
-}
-
-//
-// Structured Exception handling
-//
-
-std::wstring generate_minidump_name(const wchar_t* dumpPath)
-{
- DWORD pid = GetCurrentProcessId();
- wchar_t pname[100];
- if (GetModuleBaseName(GetCurrentProcess(), NULL, pname, _countof(pname)) == 0)
- return std::wstring();
-
- // find an available name:
- wchar_t dmpFile[MAX_PATH];
- int count = 0;
- _snwprintf_s(dmpFile, _TRUNCATE, L"%s\\%s-%lu.dmp", dumpPath, pname, pid);
- while (winapi::ex::wide::fileExists(dmpFile)) {
- if (++count > 99)
- return std::wstring();
- _snwprintf_s(dmpFile, _TRUNCATE, L"%s\\%s-%lu_%02d.dmp", dumpPath, pname, pid,
- count);
- }
- return dmpFile;
-}
-
-int createMiniDumpImpl(PEXCEPTION_POINTERS exceptionPtrs, CrashDumpsType type,
- const wchar_t* dumpPath, HMODULE dbgDLL)
-{
- typedef BOOL(WINAPI * FuncMiniDumpWriteDump)(
- HANDLE process, DWORD pid, HANDLE file, MINIDUMP_TYPE dumpType,
- const PMINIDUMP_EXCEPTION_INFORMATION exceptionParam,
- const PMINIDUMP_USER_STREAM_INFORMATION userStreamParam,
- const PMINIDUMP_CALLBACK_INFORMATION callbackParam);
-
- // notice we avoid logging here on purpose because this is called from the VEHandler
- // and the logger can crash it in extreme cases.
- // additionally it is also called for MO crashes which use it's own logging.
- winapi::ex::wide::createPath(dumpPath);
-
- auto dmpName = generate_minidump_name(dumpPath);
- if (dmpName.empty())
- return 4;
-
- FuncMiniDumpWriteDump funcDump = reinterpret_cast<FuncMiniDumpWriteDump>(
- GetProcAddress(dbgDLL, "MiniDumpWriteDump"));
- if (!funcDump)
- return 5;
-
- HANDLE dumpFile = winapi::wide::createFile(dmpName)
- .createAlways()
- .access(GENERIC_WRITE)
- .share(FILE_SHARE_WRITE)();
- if (dumpFile != INVALID_HANDLE_VALUE) {
- DWORD dumpType = MiniDumpNormal | MiniDumpWithHandleData |
- MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData;
- if (type == CrashDumpsType::Data)
- dumpType |= MiniDumpWithDataSegs;
- if (type == CrashDumpsType::Full)
- dumpType |= MiniDumpWithFullMemory;
-
- _MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
- exceptionInfo.ThreadId = GetCurrentThreadId();
- exceptionInfo.ExceptionPointers = exceptionPtrs;
- exceptionInfo.ClientPointers = FALSE;
-
- BOOL success = funcDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile,
- static_cast<MINIDUMP_TYPE>(dumpType), &exceptionInfo,
- nullptr, nullptr);
-
- CloseHandle(dumpFile);
-
- return success ? 0 : 7;
- } else
- return 6;
-}
-
-int WINAPI usvfsCreateMiniDump(PEXCEPTION_POINTERS exceptionPtrs, CrashDumpsType type,
- const wchar_t* dumpPath)
-{
- if (type == CrashDumpsType::None)
- return 0;
-
- int res = 1;
- if (HMODULE dbgDLL = LoadLibraryW(L"dbghelp.dll")) {
- try {
- res = createMiniDumpImpl(exceptionPtrs, type, dumpPath, dbgDLL);
- } catch (...) {
- res = 2;
- }
- FreeLibrary(dbgDLL);
- }
- return res;
-}
-
-static bool exceptionInUSVFS(PEXCEPTION_POINTERS exceptionPtrs)
-{
- if (!dllModule) // shouldn't happen, check just in case
- return true; // create dump to better understand how this could happen
-
- std::pair<uintptr_t, uintptr_t> range = winapi::ex::getSectionRange(dllModule);
-
- uintptr_t exceptionAddress =
- reinterpret_cast<uintptr_t>(exceptionPtrs->ExceptionRecord->ExceptionAddress);
-
- return range.first <= exceptionAddress && exceptionAddress < range.second;
-}
-
-LONG WINAPI VEHandler(PEXCEPTION_POINTERS exceptionPtrs)
-{
- // NOTICE: don't use logger in VEHandler as it can cause another fault causing
- // VEHandler to be called again and so on.
-
- if ((exceptionPtrs->ExceptionRecord->ExceptionCode < 0x80000000) // non-critical
- ||
- (exceptionPtrs->ExceptionRecord->ExceptionCode == 0xe06d7363)) { // cpp exception
- // don't report non-critical exceptions
- return EXCEPTION_CONTINUE_SEARCH;
- }
- /*
- if (((exceptionPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0)
- || (exceptionPtrs->ExceptionRecord->ExceptionCode == 0xe06d7363)) {
- // don't want to break on non-critical exceptions. 0xe06d7363 indicates a C++
- exception. why are those marked non-continuable? return EXCEPTION_CONTINUE_SEARCH;
- }
- */
-
- // VEHandler is called on "first-chance" exceptions which might be caught and handled.
- // Ideally we would like to use an UnhandledExceptionFilter but that fails to catch
- // crashes inside our hooks at least on x64, which is the main reason why want a crash
- // collection from usvfs. As a workaround/compromise we catch vectored exception but
- // only ones that originate directly within the usvfs code:
- if (!exceptionInUSVFS(exceptionPtrs))
- return EXCEPTION_CONTINUE_SEARCH;
-
- // disable our hooking mechanism to increase chances the dump writing won't crash
- HookLib::TrampolinePool& trampPool = HookLib::TrampolinePool::instance();
- if (&trampPool) { // need to test this in case of crash before TrampolinePool
- // initialized
- trampPool.forceUnlockBarrier();
- trampPool.setBlock(true);
- }
-
- usvfsCreateMiniDump(exceptionPtrs, usvfs_dump_type, usvfs_dump_path.c_str());
-
- return EXCEPTION_CONTINUE_SEARCH;
-}
-
-//
-// Exported functions
-//
-
-void __cdecl InitHooks(LPVOID parameters, size_t)
-{
- InitLoggingInternal(false, true);
-
- const usvfsParameters* params = reinterpret_cast<usvfsParameters*>(parameters);
-
- // there is already a wait in the constructor of HookManager, but this one is useful
- // to debug code here (from experience... ), should not wait twice since the second
- // will return true immediately
- if (params->debugMode) {
- while (!::IsDebuggerPresent()) {
- // wait for debugger to attach
- ::Sleep(100);
- }
- }
-
- usvfs_dump_type = params->crashDumpsType;
- usvfs_dump_path =
- ush::string_cast<std::wstring>(params->crashDumpsPath, ush::CodePage::UTF8);
-
- if (params->delayProcessMs > 0) {
- ::Sleep(static_cast<unsigned long>(params->delayProcessMs));
- }
-
- SetLogLevel(params->logLevel);
-
- if (exceptionHandler == nullptr) {
- if (usvfs_dump_type != CrashDumpsType::None)
- exceptionHandler = ::AddVectoredExceptionHandler(0, VEHandler);
- } else {
- spdlog::get("usvfs")->info("vectored exception handler already active");
- // how did this happen??
- }
-
- spdlog::get("usvfs")->info(
- "inithooks called {0} in process {1}:{2} (log level {3}, dump type {4}, dump "
- "path {5})",
- params->instanceName, winapi::ansi::getModuleFileName(nullptr),
- ::GetCurrentProcessId(), static_cast<int>(params->logLevel),
- static_cast<int>(params->crashDumpsType), params->crashDumpsPath);
-
- try {
- manager = new usvfs::HookManager(*params, dllModule);
-
- auto context = manager->context();
- auto exePath = boost::dll::program_location();
- auto libraries = context->librariesToForceLoad(exePath.filename().c_str());
- for (auto library : libraries) {
- if (std::filesystem::exists(library)) {
- const auto ret = LoadLibraryExW(library.c_str(), NULL, 0);
- if (ret) {
- spdlog::get("usvfs")->info("inithooks succeeded to force load {0}",
- ush::string_cast<std::string>(library).c_str());
- } else {
- spdlog::get("usvfs")->critical(
- "inithooks failed to force load {0}",
- ush::string_cast<std::string>(library).c_str());
- }
- }
- }
-
- spdlog::get("usvfs")->info("inithooks in process {0} successful",
- ::GetCurrentProcessId());
-
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->debug("failed to initialise hooks: {0}", e.what());
- }
-}
-
-void WINAPI usvfsGetCurrentVFSName(char* buffer, size_t size)
-{
- ush::strncpy_sz(buffer, context->callParameters().currentSHMName, size);
-}
-
-BOOL WINAPI usvfsCreateVFS(const usvfsParameters* p)
-{
- usvfs::HookContext::remove(p->instanceName);
- return usvfsConnectVFS(p);
-}
-
-BOOL WINAPI usvfsConnectVFS(const usvfsParameters* params)
-{
- if (spdlog::get("usvfs").get() == nullptr) {
- // create temporary logger so we don't get null-pointer exceptions
- spdlog::create<spdlog::sinks::null_sink_mt>("usvfs");
- }
-
- try {
- usvfsDisconnectVFS();
- context = new usvfs::HookContext(*params, dllModule);
-
- return TRUE;
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->debug("failed to connect to vfs: {}", e.what());
- return FALSE;
- }
-}
-
-void WINAPI usvfsDisconnectVFS()
-{
- if (spdlog::get("usvfs").get() == nullptr) {
- // create temporary logger so we don't get null-pointer exceptions
- spdlog::create<spdlog::sinks::null_sink_mt>("usvfs");
- }
-
- spdlog::get("usvfs")->debug("remove from process {}", GetCurrentProcessId());
-
- if (manager != nullptr) {
- delete manager;
- manager = nullptr;
- }
-
- if (context != nullptr) {
- delete context;
- context = nullptr;
- spdlog::get("usvfs")->debug("vfs unloaded");
- }
-}
-
-bool processStillActive(DWORD pid)
-{
- HANDLE proc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
-
- if (proc == nullptr) {
- return false;
- }
-
- ON_BLOCK_EXIT([&]() {
- if (proc != INVALID_HANDLE_VALUE)
- ::CloseHandle(proc);
- });
-
- DWORD exitCode;
- if (!GetExitCodeProcess(proc, &exitCode)) {
- spdlog::get("usvfs")->warn("failed to query exit code on process {}: {}", pid,
- ::GetLastError());
- return false;
- } else {
- return exitCode == STILL_ACTIVE;
- }
-}
-
-BOOL WINAPI usvfsGetVFSProcessList(size_t* count, LPDWORD processIDs)
-{
- if (count == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
-
- if (context == nullptr) {
- *count = 0;
- } else {
- std::vector<DWORD> pids = context->registeredProcesses();
- size_t realCount = 0;
- for (DWORD pid : pids) {
- if (processStillActive(pid)) {
- if ((realCount < *count) && (processIDs != nullptr)) {
- processIDs[realCount] = pid;
- }
-
- ++realCount;
- } // else the process has already ended
- }
- *count = realCount;
- }
- return TRUE;
-}
-
-BOOL WINAPI usvfsGetVFSProcessList2(size_t* count, DWORD** buffer)
-{
- if (!count || !buffer) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
-
- *count = 0;
- *buffer = nullptr;
-
- std::vector<DWORD> pids = context->registeredProcesses();
- auto last = std::remove_if(pids.begin(), pids.end(), [](DWORD id) {
- return !processStillActive(id);
- });
-
- pids.erase(last, pids.end());
-
- if (pids.empty()) {
- return TRUE;
- }
-
- *count = pids.size();
- *buffer = static_cast<DWORD*>(std::calloc(pids.size(), sizeof(DWORD)));
-
- std::copy(pids.begin(), pids.end(), *buffer);
-
- return TRUE;
-}
-
-void WINAPI usvfsClearVirtualMappings()
-{
- context->redirectionTable()->clear();
- context->inverseTable()->clear();
-}
-
-/// ensure the specified path exists. If a physical path of the same name
-/// exists, it is inserted into the virtual directory as an empty reference. If
-/// the path doesn't exist virtually and can't be cloned from a physical
-/// directory, this returns false
-/// \todo if this fails (i.e. not all intermediate directories exists) any
-/// intermediate directories already created aren't removed
-bool assertPathExists(usvfs::RedirectionTreeContainer& table, LPCWSTR path)
-{
- bfs::path p(path);
- p = p.parent_path();
-
- usvfs::RedirectionTree::NodeT* current = table.get();
-
- for (auto iter = p.begin(); iter != p.end(); iter = ush::nextIter(iter, p.end())) {
- if (current->exists(iter->string().c_str())) {
- // subdirectory exists virtually, all good
- usvfs::RedirectionTree::NodePtrT found = current->node(iter->string().c_str());
- current = found.get().get();
- } else {
- // targetPath is relative to the last rerouted "real" path. This means
- // that if virtual c:/foo maps to real c:/windows then creating virtual
- // c:/foo/bar will map to real c:/windows/bar
- bfs::path targetPath = current->data().linkTarget.size() > 0
- ? bfs::path(current->data().linkTarget.c_str()) / *iter
- : *iter / "\\";
-
- // is_directory returns false for symlinks and reparse points,
- // which causes this function to fail if the target path contains
- // either of those. paths containing reparse points is a common
- // scenario when running under Wine, so check for those explicitly.
- // this check could have a false positive if the path contains a
- // symlink to a file, but such a scenario is extremely unlikely.
- if (is_directory(targetPath) || is_symlink(targetPath) ||
- status(targetPath).type() == bfs::file_type::reparse_file) {
- usvfs::RedirectionTree::NodePtrT newNode =
- table.addDirectory(current->path() / *iter, targetPath.string().c_str(),
- ush::FLAG_DUMMY, false);
- current = newNode.get().get();
- } else {
- spdlog::get("usvfs")->info("{} doesn't exist", targetPath.c_str());
- return false;
- }
- }
- }
-
- return true;
-}
-
-static bool fileNameInSkipSuffixes(const std::string& fileNameUtf8,
- const std::vector<std::string>& skipFileSuffixes)
-{
- for (const auto& skipFileSuffix : skipFileSuffixes) {
- if (boost::algorithm::iends_with(fileNameUtf8, skipFileSuffix)) {
- spdlog::get("usvfs")->debug(
- "file '{}' should be skipped, matches file suffix '{}'", fileNameUtf8,
- skipFileSuffix);
- return true;
- }
- }
- return false;
-}
-
-static bool fileNameInSkipDirectories(const std::string& directoryNameUtf8,
- const std::vector<std::string>& skipDirectories)
-{
- for (const auto& skipDir : skipDirectories) {
- if (boost::algorithm::iequals(directoryNameUtf8, skipDir)) {
- spdlog::get("usvfs")->debug("directory '{}' should be skipped",
- directoryNameUtf8);
- return true;
- }
- }
- return false;
-}
-
-BOOL WINAPI usvfsVirtualLinkFile(LPCWSTR source, LPCWSTR destination,
- unsigned int flags)
-{
- // TODO difference between winapi and ntdll api regarding system32 vs syswow64
- // (and other windows links?)
- try {
- if (!assertPathExists(context->redirectionTable(), destination)) {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- const auto skipFileSuffixes = context->skipFileSuffixes();
-
- std::string sourceU8 = ush::string_cast<std::string>(source, ush::CodePage::UTF8);
-
- // Check if the file should be skipped
- if (fileNameInSkipSuffixes(sourceU8, skipFileSuffixes)) {
- // return false when we want to fail when the file is skipped
- return (flags & LINKFLAG_FAILIFSKIPPED) ? FALSE : TRUE;
- }
-
- auto res = context->redirectionTable().addFile(
- bfs::path(destination), usvfs::RedirectionDataLocal(sourceU8),
- !(flags & LINKFLAG_FAILIFEXISTS));
-
- if (shouldAddToInverseTree(sourceU8)) {
- std::string destinationU8 =
- ush::string_cast<std::string>(destination, ush::CodePage::UTF8);
-
- context->inverseTable().addFile(bfs::path(source),
- usvfs::RedirectionDataLocal(destinationU8), true);
- }
-
- context->updateParameters();
-
- if (res.get() == nullptr) {
- // the tree structure currently doesn't provide useful error codes but
- // this is currently the only reason
- // we would return a nullptr.
- SetLastError(ERROR_FILE_EXISTS);
- return FALSE;
- } else {
- return TRUE;
- }
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->error("failed to copy file {}", e.what());
- // TODO: no clue what's wrong
- SetLastError(ERROR_INVALID_DATA);
- return FALSE;
- }
-}
-
-/**
- * @brief extract the flags relevant to redirection
- */
-static usvfs::shared::TreeFlags convertRedirectionFlags(unsigned int flags)
-{
- usvfs::shared::TreeFlags result = 0;
- if (flags & LINKFLAG_CREATETARGET) {
- result |= usvfs::shared::FLAG_CREATETARGET;
- }
- return result;
-}
-
-BOOL WINAPI usvfsVirtualLinkDirectoryStatic(LPCWSTR source, LPCWSTR destination,
- unsigned int flags)
-{
- // TODO change notification not yet implemented
- try {
- if ((flags & LINKFLAG_FAILIFEXISTS) && winapi::ex::wide::fileExists(destination)) {
- SetLastError(ERROR_FILE_EXISTS);
- return FALSE;
- }
-
- if (!assertPathExists(context->redirectionTable(), destination)) {
- SetLastError(ERROR_PATH_NOT_FOUND);
- return FALSE;
- }
-
- std::string sourceU8 =
- ush::string_cast<std::string>(source, ush::CodePage::UTF8) + "\\";
-
- context->redirectionTable().addDirectory(
- destination, usvfs::RedirectionDataLocal(sourceU8),
- usvfs::shared::FLAG_DIRECTORY | convertRedirectionFlags(flags),
- (flags & LINKFLAG_CREATETARGET) != 0);
-
- const auto skipDirectories = context->skipDirectories();
- const auto skipFileSuffixes = context->skipFileSuffixes();
-
- if ((flags & LINKFLAG_RECURSIVE) != 0) {
- std::wstring sourceP(source);
- std::wstring sourceW = sourceP + L"\\";
- std::wstring destinationW = std::wstring(destination) + L"\\";
- if (sourceP.length() >= MAX_PATH && !ush::startswith(sourceP.c_str(), LR"(\\?\)"))
- sourceP = LR"(\\?\)" + sourceP;
-
- for (winapi::ex::wide::FileResult file :
- winapi::ex::wide::quickFindFiles(sourceP.c_str(), L"*")) {
- if (file.attributes & FILE_ATTRIBUTE_DIRECTORY) {
- if ((file.fileName != L".") && (file.fileName != L"..")) {
-
- const auto nameU8 = ush::string_cast<std::string>(file.fileName.c_str(),
- ush::CodePage::UTF8);
- // Check if the directory should be skipped
- if (fileNameInSkipDirectories(nameU8, skipDirectories)) {
- // Fail if we desire to fail when a dir/file is skipped
- if (flags & LINKFLAG_FAILIFSKIPPED) {
- spdlog::get("usvfs")->debug(
- "directory '{}' skipped, failing as defined by link flags", nameU8);
- return FALSE;
- }
-
- continue;
- }
-
- usvfsVirtualLinkDirectoryStatic((sourceW + file.fileName).c_str(),
- (destinationW + file.fileName).c_str(),
- flags);
- }
- } else {
- const auto nameU8 =
- ush::string_cast<std::string>(file.fileName.c_str(), ush::CodePage::UTF8);
-
- // Check if the file should be skipped
- if (fileNameInSkipSuffixes(nameU8, skipFileSuffixes)) {
- // Fail if we desire to fail when a dir/file is skipped
- if (flags & LINKFLAG_FAILIFSKIPPED) {
- spdlog::get("usvfs")->debug(
- "file '{}' skipped, failing as defined by link flags", nameU8);
- return FALSE;
- }
-
- continue;
- }
-
- // TODO could save memory here by storing only the file name for the
- // source and constructing the full name using the parent directory
- context->redirectionTable().addFile(
- bfs::path(destination) / nameU8,
- usvfs::RedirectionDataLocal(sourceU8 + nameU8), true);
-
- if (shouldAddToInverseTree(nameU8)) {
- std::string destinationU8 =
- ush::string_cast<std::string>(destination, ush::CodePage::UTF8) + "\\";
-
- context->inverseTable().addFile(
- bfs::path(source) / nameU8,
- usvfs::RedirectionDataLocal(destinationU8 + nameU8), true);
- }
- }
- }
- }
-
- context->updateParameters();
-
- return TRUE;
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->error("failed to copy file {}", e.what());
- // TODO: no clue what's wrong
- SetLastError(ERROR_INVALID_DATA);
- return FALSE;
- }
-}
-
-BOOL WINAPI usvfsCreateProcessHooked(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags,
- LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
- LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation)
-{
- BOOL susp = dwCreationFlags & CREATE_SUSPENDED;
- DWORD flags = dwCreationFlags | CREATE_SUSPENDED;
-
- BOOL blacklisted = context->executableBlacklisted(lpApplicationName, lpCommandLine);
-
- BOOL res = CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
- lpThreadAttributes, bInheritHandles, flags, lpEnvironment,
- lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
- if (!res) {
- spdlog::get("usvfs")->error("failed to spawn {}",
- ush::string_cast<std::string>(lpCommandLine));
- return FALSE;
- }
-
- if (!blacklisted) {
- std::wstring applicationDirPath = winapi::wide::getModuleFileName(dllModule);
- boost::filesystem::path p(applicationDirPath);
- try {
- usvfs::injectProcess(p.parent_path().wstring(), context->callParameters(),
- *lpProcessInformation);
- } catch (const std::exception& e) {
- spdlog::get("usvfs")->error("failed to inject: {}", e.what());
- logExtInfo(e, LogLevel::Error);
- ::TerminateProcess(lpProcessInformation->hProcess, 1);
- ::SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- }
-
- if (!susp) {
- ResumeThread(lpProcessInformation->hThread);
- }
-
- return TRUE;
-}
-
-BOOL WINAPI usvfsCreateVFSDump(LPSTR buffer, size_t* size)
-{
- assert(size != nullptr);
- std::ostringstream output;
- usvfs::shared::dumpTree(output, *context->redirectionTable().get());
- std::string str = output.str();
- if ((buffer != NULL) && (*size > 0)) {
- strncpy_s(buffer, *size, str.c_str(), _TRUNCATE);
- }
- bool success = *size >= str.length();
- *size = str.length();
- return success ? TRUE : FALSE;
-}
-
-VOID WINAPI usvfsBlacklistExecutable(LPCWSTR executableName)
-{
- context->blacklistExecutable(executableName);
-}
-
-VOID WINAPI usvfsClearExecutableBlacklist()
-{
- context->clearExecutableBlacklist();
-}
-
-VOID WINAPI usvfsAddSkipFileSuffix(LPCWSTR fileSuffix)
-{
- context->addSkipFileSuffix(fileSuffix);
-}
-
-VOID WINAPI usvfsClearSkipFileSuffixes()
-{
- context->clearSkipFileSuffixes();
-}
-
-VOID WINAPI usvfsAddSkipDirectory(LPCWSTR directory)
-{
- context->addSkipDirectory(directory);
-}
-
-VOID WINAPI usvfsClearSkipDirectories()
-{
- context->clearSkipDirectories();
-}
-
-VOID WINAPI usvfsForceLoadLibrary(LPCWSTR processName, LPCWSTR libraryPath)
-{
- context->forceLoadLibrary(processName, libraryPath);
-}
-
-VOID WINAPI usvfsClearLibraryForceLoads()
-{
- context->clearLibraryForceLoads();
-}
-
-VOID WINAPI usvfsPrintDebugInfo()
-{
- spdlog::get("usvfs")->warn("===== debug {} =====",
- context->redirectionTable().shmName());
- void* buffer = nullptr;
- size_t bufferSize = 0;
- context->redirectionTable().getBuffer(buffer, bufferSize);
- std::ostringstream temp;
- for (size_t i = 0; i < bufferSize; ++i) {
- temp << std::hex << std::setfill('0') << std::setw(2)
- << (unsigned)reinterpret_cast<char*>(buffer)[i] << " ";
- if ((i % 16) == 15) {
- spdlog::get("usvfs")->info("{}", temp.str());
- temp.str("");
- temp.clear();
- }
- }
- if (!temp.str().empty()) {
- spdlog::get("usvfs")->info("{}", temp.str());
- }
- spdlog::get("usvfs")->warn("===== / debug {} =====",
- context->redirectionTable().shmName());
-}
-
-const char* WINAPI usvfsVersionString()
-{
- return USVFS_VERSION_STRING;
-}
-
-//
-// DllMain
-//
-
-BOOL APIENTRY DllMain(HMODULE module, DWORD reasonForCall, LPVOID)
-{
- switch (reasonForCall) {
- case DLL_PROCESS_ATTACH: {
- dllModule = module;
- } break;
- case DLL_PROCESS_DETACH: {
- if (exceptionHandler)
- ::RemoveVectoredExceptionHandler(exceptionHandler);
- } break;
- case DLL_THREAD_ATTACH: {
- } break;
- case DLL_THREAD_DETACH: {
- } break;
- }
-
- return TRUE;
-}
diff --git a/libs/usvfs/src/usvfs_dll/usvfsparameters.cpp b/libs/usvfs/src/usvfs_dll/usvfsparameters.cpp
deleted file mode 100644
index 7059ec2..0000000
--- a/libs/usvfs/src/usvfs_dll/usvfsparameters.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-#include "usvfsparametersprivate.h"
-#include <algorithm>
-
-usvfsParameters::usvfsParameters()
- : debugMode(false), logLevel(LogLevel::Debug), crashDumpsType(CrashDumpsType::None),
- delayProcessMs(0)
-{
- std::fill(std::begin(instanceName), std::end(instanceName), 0);
- std::fill(std::begin(currentSHMName), std::end(currentSHMName), 0);
- std::fill(std::begin(currentInverseSHMName), std::end(currentInverseSHMName), 0);
- std::fill(std::begin(crashDumpsPath), std::end(crashDumpsPath), 0);
-}
-
-usvfsParameters::usvfsParameters(const char* instanceName, const char* currentSHMName,
- const char* currentInverseSHMName, bool debugMode,
- LogLevel logLevel, CrashDumpsType crashDumpsType,
- const char* crashDumpsPath, int delayProcessMs)
- : usvfsParameters()
-{
- strncpy_s(this->instanceName, instanceName, _TRUNCATE);
- strncpy_s(this->currentSHMName, currentSHMName, _TRUNCATE);
- strncpy_s(this->currentInverseSHMName, currentInverseSHMName, _TRUNCATE);
- this->debugMode = debugMode;
- this->logLevel = logLevel;
- this->crashDumpsType = crashDumpsType;
- strncpy_s(this->crashDumpsPath, crashDumpsPath, _TRUNCATE);
- this->delayProcessMs = delayProcessMs;
-}
-
-usvfsParameters::usvfsParameters(const USVFSParameters& oldParams)
- : usvfsParameters(oldParams.instanceName, oldParams.currentSHMName,
- oldParams.currentInverseSHMName, oldParams.debugMode,
- oldParams.logLevel, oldParams.crashDumpsType,
- oldParams.crashDumpsPath, 0)
-{}
-
-void usvfsParameters::setInstanceName(const char* name)
-{
- strncpy_s(instanceName, name, _TRUNCATE);
- strncpy_s(currentSHMName, 60, name, _TRUNCATE);
- memset(currentInverseSHMName, '\0', _countof(currentInverseSHMName));
- _snprintf(currentInverseSHMName, 60, "inv_%s", name);
-}
-
-void usvfsParameters::setDebugMode(bool b)
-{
- debugMode = b;
-}
-
-void usvfsParameters::setLogLevel(LogLevel level)
-{
- logLevel = level;
-}
-
-void usvfsParameters::setCrashDumpType(CrashDumpsType type)
-{
- crashDumpsType = type;
-}
-
-void usvfsParameters::setCrashDumpPath(const char* path)
-{
- if (path && *path && strlen(path) < _countof(crashDumpsPath)) {
- memcpy(crashDumpsPath, path, strlen(path) + 1);
- } else {
- // crashDumpsPath invalid or overflow of USVFSParameters variable so disable
- // crash dumps:
- crashDumpsPath[0] = 0;
- crashDumpsType = CrashDumpsType::None;
- }
-}
-
-void usvfsParameters::setProcessDelay(int milliseconds)
-{
- delayProcessMs = milliseconds;
-}
-
-extern "C"
-{
-
- const char* usvfsLogLevelToString(LogLevel lv)
- {
- switch (lv) {
- case LogLevel::Debug:
- return "debug";
-
- case LogLevel::Info:
- return "info";
-
- case LogLevel::Warning:
- return "warning";
-
- case LogLevel::Error:
- return "error";
-
- default:
- return "unknown";
- }
- }
-
- const char* usvfsCrashDumpTypeToString(CrashDumpsType t)
- {
- switch (t) {
- case CrashDumpsType::None:
- return "none";
-
- case CrashDumpsType::Mini:
- return "mini";
-
- case CrashDumpsType::Data:
- return "data";
-
- case CrashDumpsType::Full:
- return "full";
-
- default:
- return "unknown";
- }
- }
-
- usvfsParameters* usvfsCreateParameters()
- {
- return new (std::nothrow) usvfsParameters;
- }
-
- usvfsParameters* usvfsDupeParameters(usvfsParameters* p)
- {
- if (!p) {
- return nullptr;
- }
-
- auto* dupe = usvfsCreateParameters();
- if (!dupe) {
- return nullptr;
- }
-
- *dupe = *p;
-
- return dupe;
- }
-
- void usvfsCopyParameters(const usvfsParameters* source, usvfsParameters* dest)
- {
- *dest = *source;
- }
-
- void usvfsFreeParameters(usvfsParameters* p)
- {
- delete p;
- }
-
- void usvfsSetInstanceName(usvfsParameters* p, const char* name)
- {
- if (p) {
- p->setInstanceName(name);
- }
- }
-
- void usvfsSetDebugMode(usvfsParameters* p, BOOL debugMode)
- {
- if (p) {
- p->setDebugMode(debugMode);
- }
- }
-
- void usvfsSetLogLevel(usvfsParameters* p, LogLevel level)
- {
- if (p) {
- p->setLogLevel(level);
- }
- }
-
- void usvfsSetCrashDumpType(usvfsParameters* p, CrashDumpsType type)
- {
- if (p) {
- p->setCrashDumpType(type);
- }
- }
-
- void usvfsSetCrashDumpPath(usvfsParameters* p, const char* path)
- {
- if (p) {
- p->setCrashDumpPath(path);
- }
- }
-
- void usvfsSetProcessDelay(usvfsParameters* p, int milliseconds)
- {
- if (p) {
- p->setProcessDelay(milliseconds);
- }
- }
-
-} // extern "C"
diff --git a/libs/usvfs/src/usvfs_dll/version.rc b/libs/usvfs/src/usvfs_dll/version.rc
deleted file mode 100644
index 7dbf8e3..0000000
--- a/libs/usvfs/src/usvfs_dll/version.rc
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "Winver.h"
-#include "..\..\include\usvfs\usvfs_version.h"
-
-#define VER_FILEVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION
-#define VER_FILEVERSION_STR USVFS_VERSION_STRING
-
-#define VER_PRODUCTVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION
-#define VER_PRODUCTVERSION_STR USVFS_VERSION_STRING
-
-VS_VERSION_INFO VERSIONINFO
-FILEVERSION VER_FILEVERSION
-PRODUCTVERSION VER_PRODUCTVERSION
-FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-FILEFLAGS (0)
-FILEOS VOS__WINDOWS32
-FILETYPE VFT_DLL
-FILESUBTYPE VFT2_UNKNOWN
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904B0"
- BEGIN
- VALUE "FileVersion", VER_FILEVERSION_STR
- VALUE "CompanyName", "Mod Organizer 2 Team\0"
- VALUE "FileDescription", "USVFS\0"
-#ifdef _WIN64
- VALUE "OriginalFilename", "usvfs_x64.dll\0"
-#else
- VALUE "OriginalFilename", "usvfs_x86.dll\0"
-#endif
- VALUE "ProductName", "USVFS\0"
- VALUE "ProductVersion", VER_PRODUCTVERSION_STR
- END
- END
-
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0409L, 1200
- END
-END