aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src
diff options
context:
space:
mode:
Diffstat (limited to 'libs/usvfs/src')
-rw-r--r--libs/usvfs/src/shared/CMakeLists.txt23
-rw-r--r--libs/usvfs/src/shared/addrtools.h64
-rw-r--r--libs/usvfs/src/shared/directory_tree.cpp47
-rw-r--r--libs/usvfs/src/shared/directory_tree.h650
-rw-r--r--libs/usvfs/src/shared/exceptionex.cpp82
-rw-r--r--libs/usvfs/src/shared/exceptionex.h99
-rw-r--r--libs/usvfs/src/shared/formatters.h158
-rw-r--r--libs/usvfs/src/shared/loghelpers.cpp96
-rw-r--r--libs/usvfs/src/shared/loghelpers.h123
-rw-r--r--libs/usvfs/src/shared/ntdll_declarations.cpp75
-rw-r--r--libs/usvfs/src/shared/ntdll_declarations.h612
-rw-r--r--libs/usvfs/src/shared/pch.cpp1
-rw-r--r--libs/usvfs/src/shared/pch.h87
-rw-r--r--libs/usvfs/src/shared/shared_memory.h53
-rw-r--r--libs/usvfs/src/shared/shmlogger.cpp205
-rw-r--r--libs/usvfs/src/shared/shmlogger.h105
-rw-r--r--libs/usvfs/src/shared/stringcast.cpp40
-rw-r--r--libs/usvfs/src/shared/stringcast.h170
-rw-r--r--libs/usvfs/src/shared/stringutils.cpp146
-rw-r--r--libs/usvfs/src/shared/stringutils.h57
-rw-r--r--libs/usvfs/src/shared/tree_container.h630
-rw-r--r--libs/usvfs/src/shared/unicodestring.cpp77
-rw-r--r--libs/usvfs/src/shared/unicodestring.h110
-rw-r--r--libs/usvfs/src/shared/wildcard.cpp194
-rw-r--r--libs/usvfs/src/shared/wildcard.h67
-rw-r--r--libs/usvfs/src/shared/winapi.cpp490
-rw-r--r--libs/usvfs/src/shared/winapi.h610
-rw-r--r--libs/usvfs/src/shared/windows_sane.h28
-rw-r--r--libs/usvfs/src/thooklib/CMakeLists.txt14
-rw-r--r--libs/usvfs/src/thooklib/asmjit_sane.h29
-rw-r--r--libs/usvfs/src/thooklib/hooklib.cpp740
-rw-r--r--libs/usvfs/src/thooklib/hooklib.h125
-rw-r--r--libs/usvfs/src/thooklib/pch.cpp1
-rw-r--r--libs/usvfs/src/thooklib/ttrampolinepool.cpp601
-rw-r--r--libs/usvfs/src/thooklib/ttrampolinepool.h222
-rw-r--r--libs/usvfs/src/thooklib/udis86wrapper.cpp102
-rw-r--r--libs/usvfs/src/thooklib/udis86wrapper.h62
-rw-r--r--libs/usvfs/src/thooklib/utility.cpp86
-rw-r--r--libs/usvfs/src/thooklib/utility.h35
-rw-r--r--libs/usvfs/src/tinjectlib/CMakeLists.txt8
-rw-r--r--libs/usvfs/src/tinjectlib/asmjit_sane.h32
-rw-r--r--libs/usvfs/src/tinjectlib/injectlib.cpp482
-rw-r--r--libs/usvfs/src/tinjectlib/injectlib.h50
-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
-rw-r--r--libs/usvfs/src/usvfs_helper/CMakeLists.txt6
-rw-r--r--libs/usvfs/src/usvfs_helper/inject.cpp193
-rw-r--r--libs/usvfs/src/usvfs_helper/inject.h51
-rw-r--r--libs/usvfs/src/usvfs_proxy/CMakeLists.txt15
-rw-r--r--libs/usvfs/src/usvfs_proxy/main.cpp221
-rw-r--r--libs/usvfs/src/usvfs_proxy/version.rc40
73 files changed, 0 insertions, 15674 deletions
diff --git a/libs/usvfs/src/shared/CMakeLists.txt b/libs/usvfs/src/shared/CMakeLists.txt
deleted file mode 100644
index b1a7c05..0000000
--- a/libs/usvfs/src/shared/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(Boost CONFIG REQUIRED COMPONENTS algorithm interprocess filesystem)
-find_package(spdlog CONFIG REQUIRED)
-
-file(GLOB sources "*.cpp" "*.h")
-source_group("" FILES ${sources})
-
-add_library(shared STATIC ${sources})
-target_link_libraries(shared
- PUBLIC
- Boost::algorithm Boost::interprocess comsuppw
- Boost::filesystem spdlog::spdlog_header_only)
-target_include_directories(shared
- PUBLIC ${PROJECT_SOURCE_DIR}/include/usvfs ${CMAKE_CURRENT_SOURCE_DIR})
-target_precompile_headers(shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h)
-target_compile_options(shared PUBLIC /FI"pch.h")
-target_compile_definitions(shared PUBLIC
- SPDLOG_USE_STD_FORMAT SPDLOG_NO_NAME SPDLOG_NO_REGISTRY_MUTEX
- BOOST_INTERPROCESS_SHARED_DIR_FUNC BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE
- NOMINMAX _WINDOWS _UNICODE UNICODE
- _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
-)
diff --git a/libs/usvfs/src/shared/addrtools.h b/libs/usvfs/src/shared/addrtools.h
deleted file mode 100644
index b888ebf..0000000
--- a/libs/usvfs/src/shared/addrtools.h
+++ /dev/null
@@ -1,64 +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 "windows_sane.h"
-
-namespace usvfs::shared
-{
-
-#ifdef _M_AMD64
-typedef DWORD64 REGWORD;
-#elif _M_IX86
-typedef DWORD REGWORD;
-#endif
-
-inline LPVOID AddrAdd(LPVOID address, size_t offset)
-{
- return reinterpret_cast<LPVOID>(reinterpret_cast<LPBYTE>(address) + offset);
-}
-
-inline std::ptrdiff_t AddrDiff(LPVOID lhs, LPVOID rhs)
-{
- return reinterpret_cast<LPBYTE>(lhs) - reinterpret_cast<LPBYTE>(rhs);
-}
-
-// implicitly cast pointer to void*, from there cast to target type.
-// This is supposed to be safer than directly reinterpret-casting
-template <typename T>
-inline T void_ptr_cast(void* ptr)
-{
- return reinterpret_cast<T>(ptr);
-}
-
-template <>
-inline int64_t void_ptr_cast(void* ptr)
-{
- return reinterpret_cast<int64_t>(ptr);
-}
-
-template <>
-inline uint64_t void_ptr_cast(void* ptr)
-{
- return reinterpret_cast<uint64_t>(ptr);
-}
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/directory_tree.cpp b/libs/usvfs/src/shared/directory_tree.cpp
deleted file mode 100644
index 33d7ae1..0000000
--- a/libs/usvfs/src/shared/directory_tree.cpp
+++ /dev/null
@@ -1,47 +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 "directory_tree.h"
-#include "tree_container.h"
-
-namespace usvfs::shared
-{
-
-fs::path::iterator nextIter(const fs::path::iterator& iter,
- const fs::path::iterator& end)
-{
- fs::path::iterator next = iter;
- advanceIter(next, end);
- return next;
-}
-
-void advanceIter(fs::path::iterator& iter, const fs::path::iterator& end)
-{
- if (iter == end) {
- return;
- }
-
- ++iter;
- while (iter != end && (iter->wstring() == L"/" || iter->wstring() == L"\\" ||
- iter->wstring() == L"."))
- ++iter;
-}
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/directory_tree.h b/libs/usvfs/src/shared/directory_tree.h
deleted file mode 100644
index 1712071..0000000
--- a/libs/usvfs/src/shared/directory_tree.h
+++ /dev/null
@@ -1,650 +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 "exceptionex.h"
-#include "logging.h"
-#include "shared_memory.h"
-#include "stringutils.h"
-#include "wildcard.h"
-
-// simplify unit tests by allowing access to private members
-#ifndef PRIVATE
-#define PRIVATE private
-#endif // PRIVATE
-
-namespace usvfs::shared
-{
-
-template <typename T, typename U>
-struct SHMDataCreator
-{
- static T create(const U& source, const VoidAllocatorT& allocator)
- {
- return T(source, allocator);
- }
-};
-
-template <typename T, typename U>
-T createData(const U& source, const VoidAllocatorT& allocator)
-{
- return SHMDataCreator<T, U>::create(source, allocator);
-}
-
-template <typename T>
-T createDataEmpty(const VoidAllocatorT& allocator);
-
-template <typename T>
-void dataAssign(T& destination, const T& source);
-
-// crappy little workaround for fs::path iterating over path separators
-fs::path::iterator nextIter(const fs::path::iterator& iter,
- const fs::path::iterator& end);
-
-void advanceIter(fs::path::iterator& iter, const fs::path::iterator& end);
-
-// decomposes a path into its components
-//
-class DecomposablePath
-{
-public:
- explicit DecomposablePath(std::string s) : m_s(std::move(s)), m_begin(0), m_end(0)
- {
- m_end = nextSeparator(m_begin);
- }
-
- // move to the next component, returns false when there are no more components
- //
- bool next()
- {
- for (;;) {
- if (m_end >= m_s.size()) {
- // done
- m_begin = m_end;
- return false;
- }
-
- // move begin to one past the last separator found
- m_begin = m_end + 1;
-
- // find the next separator
- m_end = nextSeparator(m_begin);
-
- // check for components that should be ignored:
- // - empty, happens when the path ends with a separator
- // - slashes, happens with consecutive separators
- // - dot, unnecessary
- const auto c = current();
- if (!c.empty() && c != "\\" && c != "/" && c != ".") {
- return true;
- }
- }
- }
-
- // checks if next() would return false
- //
- bool peekNext() const
- {
- auto copy = *this;
- return copy.next();
- }
-
- // the current component, empty when next() returned false
- //
- std::string_view current() const { return {m_s.data() + m_begin, m_end - m_begin}; }
-
-private:
- const std::string m_s;
- std::size_t m_begin, m_end;
-
- // finds the next path separator
- //
- std::size_t nextSeparator(std::size_t from) const
- {
- while (from < m_s.size()) {
- if (m_s[from] == '/' || m_s[from] == '\\') {
- break;
- }
-
- ++from;
- }
-
- return from;
- }
-};
-
-namespace bi = boost::interprocess;
-namespace bmi = boost::multi_index;
-
-typedef uint8_t TreeFlags;
-
-static const TreeFlags FLAG_DIRECTORY = 0x01;
-static const TreeFlags FLAG_DUMMY = 0x02;
-static const TreeFlags FLAG_FIRSTUSERFLAG = 0x10;
-
-struct MissingThrowT
-{};
-static const MissingThrowT MissingThrow = MissingThrowT();
-
-template <typename NodeDataT>
-class TreeContainer;
-
-template <typename T1, typename T2, typename Alloc>
-struct mutable_pair
-{
- typedef T1 first_type;
- typedef T2 second_type;
-
- mutable_pair(Alloc alloc) : first(T1(alloc)), second(T2(alloc)) {}
-
- mutable_pair(const T1& f, const T2& s) : first(f), second(s) {}
-
- mutable_pair(const std::pair<T1, T2>& p) : first(p.first), second(p.second) {}
-
- T1 first;
- mutable T2 second;
-};
-
-template <typename Key, typename T, typename Compare, typename Allocator,
- typename Element = mutable_pair<Key, T, Allocator>>
-using mimap = bmi::multi_index_container<
- Element,
- bmi::indexed_by<
- bmi::ordered_unique<bmi::member<Element, Key, &Element::first>, Compare>>,
- typename Allocator::template rebind<Element>::other>;
-
-/**
- * a representation of a directory tree in memory.
- * This class is designed to be stored in shared memory.
- */
-template <typename NodeDataT>
-class DirectoryTree
-{
- template <typename T>
- friend class TreeContainer;
-
-public:
- struct CILess
- {
- template <typename U, typename V>
- bool operator()(const U& lhs, const V& rhs) const
- {
- const size_t lhsLength = getLength(lhs);
- const size_t rhsLength = getLength(rhs);
-
- const auto r =
- _strnicmp(getCharPtr(lhs), getCharPtr(rhs), std::min(lhsLength, rhsLength));
-
- if (r == 0) {
- return lhsLength < rhsLength;
- }
-
- return (r < 0);
- }
-
- private:
- const char* getCharPtr(const StringT& s) const { return s.c_str(); }
-
- const char* getCharPtr(const std::string& s) const { return s.c_str(); }
-
- const char* getCharPtr(const char* s) const { return s; }
-
- const char* getCharPtr(std::string_view s) const { return s.data(); }
-
- size_t getLength(const StringT& s) const { return s.size(); }
-
- size_t getLength(const std::string& s) const { return s.size(); }
-
- size_t getLength(const char* s) const { return strlen(s); }
-
- size_t getLength(std::string_view s) const { return s.size(); }
- };
-
- typedef DirectoryTree<NodeDataT> NodeT;
- typedef bi::deleter<NodeT, SegmentManagerT> DeleterT;
- typedef NodeDataT DataT;
-
- typedef bi::shared_ptr<NodeT, VoidAllocatorT, DeleterT> NodePtrT;
- typedef bi::weak_ptr<NodeT, VoidAllocatorT, DeleterT> WeakPtrT;
-
- typedef bi::allocator<std::pair<const StringT, NodePtrT>, SegmentManagerT>
- NodeEntryAllocatorT;
-
- typedef mimap<StringT, NodePtrT, CILess, NodeEntryAllocatorT> NodeMapT;
- typedef typename NodeMapT::iterator file_iterator;
- typedef typename NodeMapT::const_iterator const_file_iterator;
-
- typedef std::function<void(const NodePtrT&)> VisitorFunction;
-
- DirectoryTree() = delete;
- DirectoryTree(const NodeT& reference) = delete;
- DirectoryTree(NodeT&& reference) = delete;
- NodeT& operator=(NodeT reference) = delete;
-
- /**
- * @brief construct a new node to be inserted in an existing tree
- **/
- DirectoryTree(std::string_view name, TreeFlags flags, const NodePtrT& parent,
- const NodeDataT& data, const VoidAllocatorT& allocator)
- : m_Parent(parent), m_Name(name.begin(), name.end(), allocator), m_Data(data),
- m_Nodes(allocator), m_Flags(flags)
- {}
-
- ~DirectoryTree() { m_Nodes.clear(); }
-
- /**
- * @return parent node
- */
- NodePtrT parent() const { return m_Parent.lock(); }
-
- /**
- * @return the full path to the node
- */
- fs::path path() const
- {
- if (m_Parent.lock().get() == nullptr) {
- if (m_Name.size() == 0) {
- return fs::path();
- } else {
- return fs::path(m_Name.c_str()) / "\\";
- }
- } else {
- return m_Parent.lock()->path() / m_Name.c_str();
- }
- }
-
- /**
- * @return data connected to this node
- **/
- const NodeDataT& data() const { return m_Data; }
-
- /**
- * @return name of this node
- */
- std::string name() const { return m_Name.c_str(); }
-
- /**
- * @brief setFlag change a flag for this node
- * @param enabled new state for the specified flag
- */
- void setFlag(TreeFlags flag, bool enabled = true)
- {
- m_Flags = enabled ? m_Flags | flag : m_Flags & ~flag;
- }
-
- /**
- * @return true if the specified flag is set, false otherwise
- */
- bool hasFlag(TreeFlags flag) const { return (m_Flags & flag) != 0; }
-
- /**
- * @return true if this node is a directory, false if it's a regular file
- */
- bool isDirectory() const { return hasFlag(FLAG_DIRECTORY); }
-
- /**
- * @return the number of subnodes (directly) below this one
- */
- size_t numNodes() const { return m_Nodes.size(); }
-
- /**
- * @return number of nodes in this (sub-)tree including this one
- */
- size_t numNodesRecursive() const
- {
- size_t result = numNodes() + 1;
-
- for (const auto& node : m_Nodes) {
- result += node.second->numNodesRecursive();
- }
-
- return result;
- }
-
- /**
- * @brief find a node by its path
- * @param path the path to look up
- * @return a pointer to the node or a null ptr
- */
- NodePtrT findNode(const fs::path& path)
- {
- fs::path::iterator iter = path.begin();
- if (iter == path.end()) {
- return NodePtrT();
- }
- return findNode(path, iter);
- }
-
- /**
- * @brief find a node by its path
- * @param path the path to look up
- * @return a pointer to the node or a null ptr
- */
- const NodePtrT findNode(const fs::path& path) const
- {
- fs::path::iterator iter = path.begin();
- if (iter == path.end()) {
- return NodePtrT();
- }
- return findNode(path, iter);
- }
-
- /**
- * @brief visit the nodes along the specified path (in order) calling the visitor for
- * each
- * @param path the path to visit
- * @param visitor a function called for each node
- */
- void visitPath(const fs::path& path, const VisitorFunction& visitor) const
- {
- fs::path::iterator iter = path.begin();
- if (iter == path.end()) {
- return;
- }
- visitPath(path, iter, visitor);
- }
-
- /**
- * @brief retrieve a node by the specified name
- * @param name name of the node
- * @return the node found or an empty pointer if no such node was found
- */
- NodePtrT node(std::string_view name, MissingThrowT) const
- {
- auto iter = m_Nodes.find(name);
-
- if (iter != m_Nodes.end()) {
- return iter->second;
- } else {
- USVFS_THROW_EXCEPTION(node_missing_error());
- }
- }
-
- /**
- * @brief retrieve a node by the specified name
- * @param name name of the node
- * @return the node found or an empty pointer if no such node was found
- */
- NodePtrT node(std::string_view name)
- {
- auto iter = m_Nodes.find(name);
-
- if (iter != m_Nodes.end()) {
- return iter->second;
- } else {
- return NodePtrT();
- }
- }
-
- /**
- * @brief retrieve a node by the specified name
- * @param name name of the node
- * @return the node found or an empty pointer if no such node was found
- */
- const NodePtrT node(std::string_view name, MissingThrowT)
- {
- auto iter = m_Nodes.find(name);
-
- if (iter != m_Nodes.end()) {
- return iter->second;
- } else {
- USVFS_THROW_EXCEPTION(node_missing_error());
- }
- }
-
- /**
- * @brief retrieve a node by the specified name
- * @param name name of the node
- * @return the node found or an empty pointer if no such node was found
- */
- const NodePtrT node(std::string_view name) const
- {
- auto iter = m_Nodes.find(name);
-
- if (iter != m_Nodes.end()) {
- return iter->second;
- } else {
- return NodePtrT();
- }
- }
-
- /**
- * @brief test if a node by the specified name exists
- * @param name name of the node
- * @return true if the node exists, false otherwise
- */
- bool exists(std::string_view name) const
- {
- return m_Nodes.find(name) != m_Nodes.end();
- }
-
- /**
- * @brief find all matches for a pattern
- * @param pattern the pattern to look for
- * @return a vector of the found nodes
- */
- std::vector<NodePtrT> find(const std::string& pattern) const
- {
- // determine if there is a prefix in the pattern that indicates a specific
- // directory.
- size_t fixedPart = pattern.find_first_of("*?");
-
- if (fixedPart == 0)
- fixedPart = std::string::npos;
- if (fixedPart != std::string::npos)
- fixedPart = pattern.find_last_of(R"(\/)", fixedPart);
-
- std::vector<NodePtrT> result;
-
- if (fixedPart != std::string::npos) {
- // if there is a prefix, search for the node representing that path and
- // search only on that
- NodePtrT node = findNode(fs::path(pattern.substr(0, fixedPart)));
- if (node.get() != nullptr) {
- node->findLocal(result, pattern.substr(fixedPart + 1));
- }
- } else {
- findLocal(result, pattern);
- }
-
- return result;
- }
-
- /**
- * @return an iterator to the first leaf
- **/
- file_iterator filesBegin() { return m_Nodes.begin(); }
-
- /**
- * @return a const iterator to the first leaf
- **/
- const_file_iterator filesBegin() const { return m_Nodes.begin(); }
-
- /**
- * @return an iterator one past the last leaf
- **/
- file_iterator filesEnd() { return m_Nodes.end(); }
-
- /**
- * @return a const iterator one past the last leaf
- **/
- const_file_iterator filesEnd() const { return m_Nodes.end(); }
-
- /**
- * @brief erase the leaf at the specified iterator
- * @return an iterator to the following file
- **/
- file_iterator erase(file_iterator iter) { return m_Nodes.erase(iter); }
-
- /**
- * @brief clear all nodes
- */
- void clear() { m_Nodes.clear(); }
-
- void removeFromTree()
- {
- if (auto par = parent()) {
- spdlog::get("usvfs")->info("remove from tree {}", m_Name.c_str());
- auto self = par->m_Nodes.find(m_Name.c_str());
- if (self != par->m_Nodes.end()) {
- par->erase(self);
- } else {
- // trying to remove a node that does not exist, most likely because it was
- // already removed in a lower level call. this is known to happen when MoveFile
- // has the MOVEFILE_COPY_ALLOWED flag and moving a mapped file.
- spdlog::get("usvfs")->warn("Failed to remove inexisting node from tree: {}",
- m_Name.c_str());
- }
- }
- }
-
- PRIVATE : void set(StringT key, const NodePtrT& value)
- {
- auto res = m_Nodes.emplace(std::move(key), value);
- if (!res.second) {
- res.first->second = value;
- }
- }
-
- WeakPtrT findRoot() const
- {
- if (m_Parent.lock().get() == nullptr) {
- return m_Self;
- } else {
- return m_Parent.lock()->findRoot();
- }
- }
-
- NodePtrT findNode(const fs::path& name, fs::path::iterator& iter)
- {
- if (iter == name.end()) {
- return NodePtrT();
- }
-
- std::string l = iter->string();
- auto subNode = m_Nodes.find(iter->string());
- advanceIter(iter, name.end());
-
- if (iter == name.end()) {
- // last name component, should be a local node
- if (subNode != m_Nodes.end()) {
- return subNode->second;
- } else {
- return NodePtrT();
- }
- } else {
- if (subNode != m_Nodes.end()) {
- return subNode->second->findNode(name, iter);
- } else {
- return NodePtrT();
- }
- }
- }
-
- const NodePtrT findNode(const fs::path& name, fs::path::iterator& iter) const
- {
- if (iter == name.end()) {
- return NodePtrT();
- }
-
- auto subNode = m_Nodes.find(iter->string());
- advanceIter(iter, name.end());
-
- if (iter == name.end()) {
- // last name component, should be a local node
- if (subNode != m_Nodes.end()) {
- return subNode->second;
- } else {
- return NodePtrT();
- }
- } else {
- if (subNode != m_Nodes.end()) {
- return subNode->second->findNode(name, iter);
- } else {
- return NodePtrT();
- }
- }
- }
-
- void visitPath(const fs::path& path, fs::path::iterator& iter,
- const VisitorFunction& visitor) const
- {
- if (iter == path.end()) {
- return;
- }
-
- auto subNode = m_Nodes.find(iter->string());
-
- if (subNode != m_Nodes.end()) {
- visitor(subNode->second);
- advanceIter(iter, path.end());
- if (iter != path.end()) {
- subNode->second->visitPath(path, iter, visitor);
- }
- }
- }
-
- void findLocal(std::vector<NodePtrT>& output, const std::string& pattern) const
- {
- for (auto iter = m_Nodes.begin(); iter != m_Nodes.end(); ++iter) {
- LPCSTR remainder = nullptr;
-
- if (pattern.size() > 1 && (pattern[0] == '*') &&
- ((pattern[1] == '/') || (pattern[1] == '\\')) &&
- iter->second->isDirectory()) {
- // the star may represent a directory (one directory level, not
- // multiple!), search in subdirectory
- iter->second->findLocal(output, pattern.substr(1));
- } else if ((remainder = wildcard::PartialMatch(iter->second->name().c_str(),
- pattern.c_str())) != nullptr) {
- if ((*remainder == '\0') || (strcmp(remainder, "*") == 0)) {
- NodePtrT node = iter->second;
- output.push_back(node);
- }
-
- if (iter->second->isDirectory()) {
- iter->second->findLocal(output, remainder);
- }
- }
- }
- }
-
- PRIVATE : TreeFlags m_Flags;
-
- WeakPtrT m_Parent;
- WeakPtrT m_Self;
-
- StringT m_Name;
- NodeDataT m_Data;
-
- NodeMapT m_Nodes;
-};
-
-template <typename NodeDataT>
-void dumpTree(std::ostream& stream, const DirectoryTree<NodeDataT>& tree, int level = 0)
-{
- stream << std::string(level, ' ') << tree.name() << " -> " << tree.data() << "\n";
- for (auto iter = tree.filesBegin(); iter != tree.filesEnd(); ++iter) {
- dumpTree<NodeDataT>(stream, *iter->second, level + 1);
- }
-}
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/exceptionex.cpp b/libs/usvfs/src/shared/exceptionex.cpp
deleted file mode 100644
index 4a814b5..0000000
--- a/libs/usvfs/src/shared/exceptionex.cpp
+++ /dev/null
@@ -1,82 +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 "exceptionex.h"
-#include "winapi.h"
-#include <spdlog/spdlog.h>
-
-namespace usvfs::shared
-{
-
-std::string windows_error::constructMessage(const std::string& input, int inErrorCode)
-{
- std::ostringstream finalMessage;
- finalMessage << input;
-
- LPSTR buffer = nullptr;
-
- DWORD errorCode = inErrorCode != -1 ? inErrorCode : GetLastError();
-
- // TODO: the message is not english?
- if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR)&buffer, 0, nullptr) == 0) {
- finalMessage << " (errorcode " << errorCode << ")";
- } else {
- LPSTR lastChar = buffer + strlen(buffer) - 2;
- *lastChar = '\0';
- finalMessage << " (" << buffer << " [" << errorCode << "])";
- LocalFree(buffer); // allocated by FormatMessage
- }
-
- SetLastError(
- errorCode); // restore error code because FormatMessage might have modified it
- return finalMessage.str();
-}
-
-} // namespace usvfs::shared
-
-void logExtInfo(const std::exception& e, LogLevel logLevel)
-{
- std::string content;
-
- if (const std::string* msg = boost::get_error_info<ex_msg>(e)) {
- content = *msg;
- }
-
- if (const DWORD* errorCode = boost::get_error_info<ex_win_errcode>(e)) {
- content = std::string("error: ") + winapi::ex::ansi::errorString(*errorCode);
- }
-
- switch (logLevel) {
- case LogLevel::Debug:
- spdlog::get("usvfs")->debug(content);
- break;
- case LogLevel::Info:
- spdlog::get("usvfs")->info(content);
- break;
- case LogLevel::Warning:
- spdlog::get("usvfs")->warn(content);
- break;
- case LogLevel::Error:
- spdlog::get("usvfs")->error(content);
- break;
- }
-}
diff --git a/libs/usvfs/src/shared/exceptionex.h b/libs/usvfs/src/shared/exceptionex.h
deleted file mode 100644
index 4c298a5..0000000
--- a/libs/usvfs/src/shared/exceptionex.h
+++ /dev/null
@@ -1,99 +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 "logging.h"
-#include <boost/exception/all.hpp>
-#include <stdexcept>
-
-typedef boost::error_info<struct tag_message, DWORD> ex_win_errcode;
-typedef boost::error_info<struct tag_message, std::string> ex_msg;
-
-struct std_boost_exception : virtual boost::exception, virtual std::exception
-{
- const char* what() const noexcept override
- {
- return boost::diagnostic_information_what(*this);
- }
-};
-
-struct incompatibility_error : std_boost_exception
-{};
-struct usage_error : std_boost_exception
-{};
-struct data_error : std_boost_exception
-{};
-struct file_not_found_error : std_boost_exception
-{};
-struct timeout_error : std_boost_exception
-{};
-struct unknown_error : std_boost_exception
-{};
-struct node_missing_error : std_boost_exception
-{};
-
-#define USVFS_THROW_EXCEPTION(x) BOOST_THROW_EXCEPTION(x)
-
-void logExtInfo(const std::exception& e, LogLevel logLevel = LogLevel::Warning);
-
-namespace usvfs::shared
-{
-
-class windows_error : public std::runtime_error
-{
-public:
- windows_error(const std::string& message, int errorcode = GetLastError())
- : runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode)
- {}
-
- int getErrorCode() const { return m_ErrorCode; }
-
-private:
- int m_ErrorCode;
-
- std::string constructMessage(const std::string& input, int errorcode);
-};
-
-class guard
-{
-public:
- explicit guard(std::function<void()> f) : m_f(std::move(f)) {}
-
- ~guard()
- {
- if (m_f) {
- m_f();
- }
- }
-
- guard(const guard&);
- guard& operator=(const guard&);
-
-private:
- std::function<void()> m_f;
-};
-
-} // namespace usvfs::shared
-
-#define CONCATENATE_DIRECT(s1, s2) s1##s2
-#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
-#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
-#define ON_BLOCK_EXIT(f) usvfs::shared::guard ANONYMOUS_VARIABLE(guard)(f)
diff --git a/libs/usvfs/src/shared/formatters.h b/libs/usvfs/src/shared/formatters.h
deleted file mode 100644
index 3d0e47e..0000000
--- a/libs/usvfs/src/shared/formatters.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
-Userspace Virtual Filesystem
-
-Copyright (C) 2024. 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 <format>
-#include <type_traits>
-
-#include "ntdll_declarations.h"
-
-// formatters for standard types
-
-namespace usvfs::log
-{
-std::string to_string(LPCWSTR value);
-std::string to_string(PCUNICODE_STRING value);
-} // namespace usvfs::log
-
-template <class Enum, class CharT>
- requires std::is_enum_v<Enum>
-struct std::formatter<Enum, CharT> : std::formatter<std::underlying_type_t<Enum>, CharT>
-{
- template <class FmtContext>
- FmtContext::iterator format(Enum v, FmtContext& ctx) const
- {
- return std::formatter<std::underlying_type_t<Enum>, CharT>::format(
- static_cast<std::underlying_type_t<Enum>>(v), ctx);
- }
-};
-
-template <>
-struct std::formatter<LPCWSTR, char> : std::formatter<std::string, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(LPCWSTR v, FmtContext& ctx) const
- {
- return std::formatter<std::string, char>::format(usvfs::log::to_string(v), ctx);
- }
-};
-
-template <>
-struct std::formatter<std::wstring, char> : std::formatter<LPCWSTR, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(const std::wstring& v, FmtContext& ctx) const
- {
- return std::formatter<LPCWSTR, char>::format(v.c_str(), ctx);
- }
-};
-
-template <>
-struct std::formatter<PCUNICODE_STRING, char> : std::formatter<std::string, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(PCUNICODE_STRING v, FmtContext& ctx) const
- {
- return std::formatter<std::string, char>::format(usvfs::log::to_string(v), ctx);
- }
-};
-
-template <>
-struct std::formatter<UNICODE_STRING, char> : std::formatter<std::string, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(UNICODE_STRING v, FmtContext& ctx) const
- {
- return std::formatter<std::string, char>::format(usvfs::log::to_string(&v), ctx);
- }
-};
-
-template <class Pointer>
- requires(std::is_pointer_v<Pointer> && !std::is_same_v<Pointer, const char*> &&
- !std::is_same_v<Pointer, char*> && !std::is_same_v<Pointer, const void*> &&
- !std::is_same_v<Pointer, void*>)
-struct std::formatter<Pointer, char> : std::formatter<const void*, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(Pointer v, FmtContext& ctx) const
- {
- return std::formatter<const void*, char>::format(v, ctx);
- }
-};
-
-namespace usvfs::log
-{
-
-/**
- * a small helper class to wrap any object. The whole point is to give us a way
- * to ensure our own operator<< is used in addParam calls
- */
-template <typename T>
-class Wrap
-{
-public:
- explicit Wrap(const T& data) : m_Data(data) {}
- Wrap(Wrap<T>&& reference) : m_Data(std::move(reference.m_Data)) {}
-
- Wrap(const Wrap<T>& reference) = delete;
- Wrap<T>& operator=(const Wrap<T>& reference) = delete;
-
-private:
- friend struct ::std::formatter<Wrap<T>, char>;
- const T& m_Data;
-};
-
-template <typename T>
-Wrap<T> wrap(const T& data)
-{
- return Wrap<T>(data);
-}
-
-} // namespace usvfs::log
-
-template <>
-struct std::formatter<usvfs::log::Wrap<DWORD>, char> : std::formatter<DWORD, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(const usvfs::log::Wrap<DWORD>& v, FmtContext& ctx) const
- {
- return std::format_to(ctx.out(), "{:x}", v.m_Data);
- }
-};
-
-template <>
-struct std::formatter<usvfs::log::Wrap<NTSTATUS>, char> : std::formatter<NTSTATUS, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(const usvfs::log::Wrap<NTSTATUS>& v,
- FmtContext& ctx) const
- {
- switch (v.m_Data) {
- case 0x00000000:
- return std::format_to(ctx.out(), "ok");
- case 0xC0000022:
- return std::format_to(ctx.out(), "access denied");
- case 0xC0000035:
- return std::format_to(ctx.out(), "exists already");
- }
- return std::format_to(ctx.out(), "err {:x}", v.m_Data);
- }
-};
diff --git a/libs/usvfs/src/shared/loghelpers.cpp b/libs/usvfs/src/shared/loghelpers.cpp
deleted file mode 100644
index bfcabf1..0000000
--- a/libs/usvfs/src/shared/loghelpers.cpp
+++ /dev/null
@@ -1,96 +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 <format>
-
-#include "formatters.h"
-#include "loghelpers.h"
-#include "stringcast.h"
-#include "stringutils.h"
-
-namespace ush = usvfs::shared;
-
-namespace usvfs::log
-{
-
-// this is declared in formatters but is defined here to avoid a whole .cpp
-// file for two small functions
-//
-// comments from old code, taken as is:
-//
-// 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
-//
-std::string to_string(LPCWSTR value)
-{
- if (value == nullptr) {
- return "<null>";
- }
- try {
- return ush::string_cast<std::string>(value, ush::CodePage::UTF8);
- } catch (const std::exception& e) {
- return std::format("<err: {}>", e.what());
- }
-}
-std::string to_string(PCUNICODE_STRING value)
-{
- try {
- return ush::string_cast<std::string>(value->Buffer, ush::CodePage::UTF8,
- value->Length / sizeof(WCHAR));
- } catch (const std::exception& e) {
- return std::format("<err: {}>", e.what());
- }
-}
-
-spdlog::level::level_enum ConvertLogLevel(LogLevel level)
-{
- switch (level) {
- case LogLevel::Debug:
- return spdlog::level::debug;
- case LogLevel::Info:
- return spdlog::level::info;
- case LogLevel::Warning:
- return spdlog::level::warn;
- case LogLevel::Error:
- return spdlog::level::err;
- default:
- return spdlog::level::debug;
- }
-}
-
-LogLevel ConvertLogLevel(spdlog::level::level_enum level)
-{
- switch (level) {
- case spdlog::level::debug:
- return LogLevel::Debug;
- case spdlog::level::info:
- return LogLevel::Info;
- case spdlog::level::warn:
- return LogLevel::Warning;
- case spdlog::level::err:
- return LogLevel::Error;
- default:
- return LogLevel::Debug;
- }
-}
-
-} // namespace usvfs::log
diff --git a/libs/usvfs/src/shared/loghelpers.h b/libs/usvfs/src/shared/loghelpers.h
deleted file mode 100644
index 071ad9d..0000000
--- a/libs/usvfs/src/shared/loghelpers.h
+++ /dev/null
@@ -1,123 +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 "formatters.h"
-#include "ntdll_declarations.h"
-#include "shmlogger.h"
-#include "stringutils.h"
-
-namespace usvfs::log
-{
-
-enum class DisplayStyle : uint8_t
-{
- Hex = 0x01
-};
-
-class CallLoggerDummy
-{
-public:
- template <typename T>
- CallLoggerDummy& addParam(const char*, const T&, uint8_t style = 0)
- {
- return *this;
- }
-};
-
-class CallLogger
-{
-public:
- explicit CallLogger(const char* function)
- {
- const char* namespaceend = strrchr(function, ':');
-
- if (namespaceend != nullptr) {
- function = namespaceend + 1;
- }
-
- m_Message = function;
- }
-
- ~CallLogger()
- {
- try {
- static std::shared_ptr<spdlog::logger> log = spdlog::get("hooks");
- log->debug("{}", m_Message);
- } catch (...) {
- // suppress all exceptions in destructor
- }
- }
-
- template <typename T>
- CallLogger& addParam(const char* name, const T& value, uint8_t style = 0);
-
-private:
- std::string m_Message;
-};
-
-template <typename T>
-CallLogger& CallLogger::addParam(const char* name, const T& value, uint8_t style)
-{
- static bool enabled = spdlog::get("hooks")->should_log(spdlog::level::debug);
- typedef std::underlying_type<DisplayStyle>::type DSType;
-
- if (enabled) {
- if constexpr (std::is_pointer_v<T>) {
- if (value == nullptr) {
- std::format_to(std::back_inserter(m_Message), "[{}=<null>]", name);
- } else {
- std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value);
- }
- } else if constexpr (std::is_integral_v<T>) {
- if (style & static_cast<DSType>(DisplayStyle::Hex)) {
- std::format_to(std::back_inserter(m_Message), "[{}={:x}]", name, value);
- } else {
- std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value);
- }
- } else {
- std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value);
- }
- }
-
- return *this;
-}
-
-spdlog::level::level_enum ConvertLogLevel(LogLevel level);
-LogLevel ConvertLogLevel(spdlog::level::level_enum level);
-
-} // namespace usvfs::log
-
-// prefer the short variant of the function name, without signature.
-// Fall back to the portable boost macro
-#ifdef __FUNCTION__
-#define __MYFUNC__ __FUNCTION__
-#else
-#define __MYFUNC__ BOOST_CURRENT_FUNCTION
-#endif
-
-#define LOG_CALL() usvfs::log::CallLogger(__MYFUNC__)
-
-#define PARAM(val) addParam(#val, val)
-#define PARAMHEX(val) \
- addParam(#val, val, static_cast<uint8_t>(usvfs::log::DisplayStyle::Hex))
-#define PARAMWRAP(val) addParam(#val, usvfs::log::wrap(val))
diff --git a/libs/usvfs/src/shared/ntdll_declarations.cpp b/libs/usvfs/src/shared/ntdll_declarations.cpp
deleted file mode 100644
index b4bf0f5..0000000
--- a/libs/usvfs/src/shared/ntdll_declarations.cpp
+++ /dev/null
@@ -1,75 +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 "ntdll_declarations.h"
-#include <cassert>
-
-#define LOAD_EXT(mod, name) \
- name = reinterpret_cast<name##_type>(::GetProcAddress(mod, #name)); \
- assert(name != nullptr)
-
-NtQueryDirectoryFile_type NtQueryDirectoryFile;
-NtQueryDirectoryFileEx_type NtQueryDirectoryFileEx;
-NtQueryFullAttributesFile_type NtQueryFullAttributesFile;
-NtQueryAttributesFile_type NtQueryAttributesFile;
-NtQueryObject_type NtQueryObject;
-NtQueryInformationFile_type NtQueryInformationFile;
-NtQueryInformationByName_type NtQueryInformationByName;
-NtOpenFile_type NtOpenFile;
-NtCreateFile_type NtCreateFile;
-NtClose_type NtClose;
-RtlDoesFileExists_U_type RtlDoesFileExists_U;
-RtlDosPathNameToRelativeNtPathName_U_WithStatus_type
- RtlDosPathNameToRelativeNtPathName_U_WithStatus;
-RtlReleaseRelativeName_type RtlReleaseRelativeName;
-RtlGetVersion_type RtlGetVersion;
-NtTerminateProcess_type NtTerminateProcess;
-
-static bool ntdll_initialized;
-
-void ntdll_declarations_init()
-{
- if (!ntdll_initialized) {
- HMODULE ntDLLMod = GetModuleHandleW(L"ntdll.dll");
-
- LOAD_EXT(ntDLLMod, NtQueryDirectoryFile);
- LOAD_EXT(ntDLLMod, NtQueryDirectoryFileEx);
- LOAD_EXT(ntDLLMod, NtQueryFullAttributesFile);
- LOAD_EXT(ntDLLMod, NtQueryAttributesFile);
- LOAD_EXT(ntDLLMod, NtQueryObject);
- LOAD_EXT(ntDLLMod, NtQueryInformationFile);
- LOAD_EXT(ntDLLMod, NtQueryInformationByName);
- LOAD_EXT(ntDLLMod, NtCreateFile);
- LOAD_EXT(ntDLLMod, NtOpenFile);
- LOAD_EXT(ntDLLMod, NtClose);
- LOAD_EXT(ntDLLMod, RtlDoesFileExists_U);
- LOAD_EXT(ntDLLMod, RtlDosPathNameToRelativeNtPathName_U_WithStatus);
- LOAD_EXT(ntDLLMod, RtlReleaseRelativeName);
- LOAD_EXT(ntDLLMod, RtlGetVersion);
- LOAD_EXT(ntDLLMod, NtTerminateProcess);
-
- ntdll_initialized = true;
- }
-}
-
-static struct __Initializer
-{
- __Initializer() { ntdll_declarations_init(); }
-} __initializer;
diff --git a/libs/usvfs/src/shared/ntdll_declarations.h b/libs/usvfs/src/shared/ntdll_declarations.h
deleted file mode 100644
index 813ad8c..0000000
--- a/libs/usvfs/src/shared/ntdll_declarations.h
+++ /dev/null
@@ -1,612 +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 "windows_sane.h"
-
-#pragma warning(push)
-#pragma warning(disable : 4201)
-
-typedef LONG NTSTATUS;
-
-typedef struct _IO_STATUS_BLOCK
-{
- union
- {
- NTSTATUS Status;
- PVOID Pointer;
- } DUMMYUNIONNAME;
-
- ULONG_PTR Information;
-} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
-
-typedef struct _FILE_DIRECTORY_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- WCHAR FileName[1];
-} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
-
-typedef struct _FILE_FULL_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- WCHAR FileName[1];
-} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
-
-typedef struct _FILE_ID_FULL_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- LARGE_INTEGER FileId;
- WCHAR FileName[1];
-} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
-
-typedef struct _FILE_BOTH_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- CCHAR ShortNameLength;
- WCHAR ShortName[12];
- WCHAR FileName[1];
-} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
-
-typedef struct _FILE_ID_BOTH_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- CCHAR ShortNameLength;
- WCHAR ShortName[12];
- LARGE_INTEGER FileId;
- WCHAR FileName[1];
-} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION;
-
-typedef struct _FILE_BASIC_INFORMATION
-{
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- ULONG FileAttributes;
-} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
-
-typedef struct _FILE_RENAME_INFORMATION
-{
-#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS1)
- union
- {
- BOOLEAN ReplaceIfExists; // FileRenameInformation
- ULONG Flags; // FileRenameInformationEx
- } DUMMYUNIONNAME;
-#else
- BOOLEAN ReplaceIfExists;
-#endif
- HANDLE RootDirectory;
- ULONG FileNameLength;
- WCHAR FileName[1];
-} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
-
-typedef struct _FILE_STANDARD_INFORMATION
-{
- LARGE_INTEGER AllocationSize;
- LARGE_INTEGER EndOfFile;
- ULONG NumberOfLinks;
- BOOLEAN DeletePending;
- BOOLEAN Directory;
-} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
-
-typedef struct _FILE_NAMES_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- ULONG FileNameLength;
- WCHAR FileName[1];
-} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
-
-typedef struct _FILE_INTERNAL_INFORMATION
-{
- LARGE_INTEGER IndexNumber;
-} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
-
-typedef struct _FILE_EA_INFORMATION
-{
- ULONG EaSize;
-} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
-
-typedef struct _FILE_ACCESS_INFORMATION
-{
- ACCESS_MASK AccessFlags;
-} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
-
-typedef struct _FILE_POSITION_INFORMATION
-{
- LARGE_INTEGER CurrentByteOffset;
-} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
-
-typedef struct _FILE_MODE_INFORMATION
-{
- ULONG Mode;
-} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;
-
-typedef struct _FILE_ALIGNMENT_INFORMATION
-{
- ULONG AlignmentRequirement;
-} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION;
-
-typedef struct _FILE_NAME_INFORMATION
-{
- ULONG FileNameLength;
- WCHAR FileName[1];
-} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
-
-typedef struct _FILE_ID_EXTD_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- FILE_ID_128 FileId;
- WCHAR FileName[1];
-} FILE_ID_EXTD_DIR_INFORMATION, *PFILE_ID_EXTD_DIR_INFORMATION;
-
-typedef struct _FILE_ID_EXTD_BOTH_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- FILE_ID_128 FileId;
- CCHAR ShortNameLength;
- WCHAR ShortName[12];
- WCHAR FileName[1];
-} FILE_ID_EXTD_BOTH_DIR_INFORMATION, *PFILE_ID_EXTD_BOTH_DIR_INFORMATION;
-
-typedef struct _FILE_ID_64_EXTD_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- LARGE_INTEGER FileId;
- WCHAR FileName[1];
-} FILE_ID_64_EXTD_DIR_INFORMATION, *PFILE_ID_64_EXTD_DIR_INFORMATION;
-
-typedef struct _FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- LARGE_INTEGER FileId;
- CCHAR ShortNameLength;
- WCHAR ShortName[12];
- WCHAR FileName[1];
-} FILE_ID_64_EXTD_BOTH_DIR_INFORMATION, *PFILE_ID_64_EXTD_BOTH_DIR_INFORMATION;
-
-typedef struct _FILE_ID_ALL_EXTD_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- LARGE_INTEGER FileId;
- FILE_ID_128 FileId128;
- WCHAR FileName[1];
-} FILE_ID_ALL_EXTD_DIR_INFORMATION, *PFILE_ID_ALL_EXTD_DIR_INFORMATION;
-
-typedef struct _FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION
-{
- ULONG NextEntryOffset;
- ULONG FileIndex;
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER EndOfFile;
- LARGE_INTEGER AllocationSize;
- ULONG FileAttributes;
- ULONG FileNameLength;
- ULONG EaSize;
- ULONG ReparsePointTag;
- LARGE_INTEGER FileId;
- FILE_ID_128 FileId128;
- CCHAR ShortNameLength;
- WCHAR ShortName[12];
- WCHAR FileName[1];
-} FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION, *PFILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION;
-
-typedef struct _FILE_ALL_INFORMATION
-{
- FILE_BASIC_INFORMATION BasicInformation;
- FILE_STANDARD_INFORMATION StandardInformation;
- FILE_INTERNAL_INFORMATION InternalInformation;
- FILE_EA_INFORMATION EaInformation;
- FILE_ACCESS_INFORMATION AccessInformation;
- FILE_POSITION_INFORMATION PositionInformation;
- FILE_MODE_INFORMATION ModeInformation;
- FILE_ALIGNMENT_INFORMATION AlignmentInformation;
- FILE_NAME_INFORMATION NameInformation;
-} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
-
-typedef struct _FILE_OBJECTID_INFORMATION
-{
- LONGLONG FileReference;
- UCHAR ObjectId[16];
- union
- {
- struct
- {
- UCHAR BirthVolumeId[16];
- UCHAR BirthObjectId[16];
- UCHAR DomainId[16];
- };
- UCHAR ExtendedInfo[48];
- };
-} FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION;
-
-typedef struct _FILE_REPARSE_POINT_INFORMATION
-{
- LONGLONG FileReference;
- ULONG Tag;
-} FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION;
-
-// copied from ntstatus.h
-#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
-#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
-#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
-#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
-#define STATUS_NO_SUCH_FILE ((NTSTATUS)0xC000000FL)
-
-#define SL_RESTART_SCAN 0x01
-#define SL_RETURN_SINGLE_ENTRY 0x02
-#define SL_INDEX_SPECIFIED 0x04
-#define SL_RETURN_ON_DISK_ENTRIES_ONLY 0x08
-
-#define SL_QUERY_DIRECTORY_MASK 0x0b
-
-typedef enum _FILE_INFORMATION_CLASS
-{
- FileDirectoryInformation = 1,
- FileFullDirectoryInformation = 2,
- FileBothDirectoryInformation = 3,
- FileStandardInformation = 5,
- FileNameInformation = 9,
- FileRenameInformation = 10,
- FileNamesInformation = 12,
- FileAllInformation = 18,
- FileObjectIdInformation = 29,
- FileReparsePointInformation = 33,
- FileIdBothDirectoryInformation = 37,
- FileIdFullDirectoryInformation = 38,
- FileNormalizedNameInformation = 48,
- FileIdExtdDirectoryInformation = 60,
- FileIdExtdBothDirectoryInformation = 63,
- FileId64ExtdDirectoryInformation = 78,
- FileId64ExtdBothDirectoryInformation = 79,
- FileIdAllExtdDirectoryInformation = 80,
- FileIdAllExtdBothDirectoryInformation = 81
-} FILE_INFORMATION_CLASS,
- *PFILE_INFORMATION_CLASS;
-
-typedef enum _MODE
-{
- KernelMode,
- UserMode,
- MaximumMode
-} MODE;
-
-typedef struct _IO_STATUS_BLOCK IO_STATUS_BLOCK;
-
-typedef struct _IO_STATUS_BLOCK* PIO_STATUS_BLOCK;
-// typedef VOID (NTAPI *PIO_APC_ROUTINE )(__in PVOID ApcContext, __in
-// PIO_STATUS_BLOCK IoStatusBlock, __in ULONG Reserved);
-typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock,
- ULONG Reserved);
-typedef enum _FILE_INFORMATION_CLASS FILE_INFORMATION_CLASS;
-
-typedef struct _UNICODE_STRING
-{
- USHORT Length;
- USHORT MaximumLength;
- PWSTR Buffer;
-} UNICODE_STRING, *PUNICODE_STRING;
-typedef const UNICODE_STRING* PCUNICODE_STRING;
-
-typedef struct _OBJECT_ATTRIBUTES
-{
- ULONG Length;
- HANDLE RootDirectory;
- PUNICODE_STRING ObjectName;
- ULONG Attributes;
- PVOID SecurityDescriptor;
- PVOID SecurityQualityOfService;
-} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
-
-typedef enum _POOL_TYPE
-{
- NonPagedPool,
- NonPagedPoolExecute = NonPagedPool,
- PagedPool,
- NonPagedPoolMustSucceed = NonPagedPool + 2,
- DontUseThisType,
- NonPagedPoolCacheAligned = NonPagedPool + 4,
- PagedPoolCacheAligned,
- NonPagedPoolCacheAlignedMustS = NonPagedPool + 6,
- MaxPoolType,
- NonPagedPoolBase = 0,
- NonPagedPoolBaseMustSucceed = NonPagedPoolBase + 2,
- NonPagedPoolBaseCacheAligned = NonPagedPoolBase + 4,
- NonPagedPoolBaseCacheAlignedMustS = NonPagedPoolBase + 6,
- NonPagedPoolSession = 32,
- PagedPoolSession = NonPagedPoolSession + 1,
- NonPagedPoolMustSucceedSession = PagedPoolSession + 1,
- DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1,
- NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1,
- PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1,
- NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1,
- NonPagedPoolNx = 512,
- NonPagedPoolNxCacheAligned = NonPagedPoolNx + 4,
- NonPagedPoolSessionNx = NonPagedPoolNx + 32
-} POOL_TYPE;
-
-typedef struct _OBJECT_TYPE_INITIALIZER
-{
- WORD Length;
- UCHAR ObjectTypeFlags;
- ULONG CaseInsensitive : 1;
- ULONG UnnamedObjectsOnly : 1;
- ULONG UseDefaultObject : 1;
- ULONG SecurityRequired : 1;
- ULONG MaintainHandleCount : 1;
- ULONG MaintainTypeList : 1;
- ULONG ObjectTypeCode;
- ULONG InvalidAttributes;
- GENERIC_MAPPING GenericMapping;
- ULONG ValidAccessMask;
- POOL_TYPE PoolType;
- ULONG DefaultPagedPoolCharge;
- ULONG DefaultNonPagedPoolCharge;
- PVOID DumpProcedure;
- LONG* OpenProcedure;
- PVOID CloseProcedure;
- PVOID DeleteProcedure;
- LONG* ParseProcedure;
- LONG* SecurityProcedure;
- LONG* QueryNameProcedure;
- UCHAR* OkayToCloseProcedure;
-} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
-
-typedef CCHAR KPROCESSOR_MODE;
-
-typedef struct _OBJECT_HANDLE_INFORMATION
-{
- ULONG HandleAttributes;
- ACCESS_MASK GrantedAccess;
-} OBJECT_HANDLE_INFORMATION, *POBJECT_HANDLE_INFORMATION;
-
-typedef struct _OBJECT_NAME_INFORMATION
-{
- UNICODE_STRING Name;
-} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
-
-typedef enum _OBJECT_INFORMATION_CLASS
-{
- ObjectBasicInformation = 0,
- ObjectNameInformation = 1,
- ObjectTypeInformation = 2
-} OBJECT_INFORMATION_CLASS;
-
-typedef struct _RTL_RELATIVE_NAME
-{
- UNICODE_STRING RelativeName;
- HANDLE ContainingDirectory;
- void* CurDirRef;
-} RTL_RELATIVE_NAME, *PRTL_RELATIVE_NAME;
-
-typedef struct _FILE_NETWORK_OPEN_INFORMATION
-{
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- LARGE_INTEGER AllocationSize;
- LARGE_INTEGER EndOfFile;
- ULONG FileAttributes;
-} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
-
-#define FILE_DIRECTORY_FILE 0x00000001
-#define FILE_WRITE_THROUGH 0x00000002
-#define FILE_SEQUENTIAL_ONLY 0x00000004
-#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
-#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
-#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
-#define FILE_NON_DIRECTORY_FILE 0x00000040
-#define FILE_CREATE_TREE_CONNECTION 0x00000080
-#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
-#define FILE_NO_EA_KNOWLEDGE 0x00000200
-#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
-#define FILE_RANDOM_ACCESS 0x00000800
-#define FILE_DELETE_ON_CLOSE 0x00001000
-#define FILE_OPEN_BY_FILE_ID 0x00002000
-#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
-#define FILE_NO_COMPRESSION 0x00008000
-#define FILE_OPEN_REQUIRING_OPLOCK 0x00010000
-#define FILE_DISALLOW_EXCLUSIVE 0x00020000
-#define FILE_SESSION_AWARE 0x00040000
-#define FILE_RESERVE_OPFILTER 0x00100000
-#define FILE_OPEN_REPARSE_POINT 0x00200000
-#define FILE_OPEN_NO_RECALL 0x00400000
-#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
-#define FILE_CONTAINS_EXTENDED_CREATE_INFORMATION 0x10000000
-
-// Nt
-
-using NtQueryDirectoryFile_type = NTSTATUS(WINAPI*)(HANDLE, HANDLE, PIO_APC_ROUTINE,
- PVOID, PIO_STATUS_BLOCK, PVOID,
- ULONG, FILE_INFORMATION_CLASS,
- BOOLEAN, PUNICODE_STRING, BOOLEAN);
-using NtQueryDirectoryFileEx_type = NTSTATUS(WINAPI*)(HANDLE, HANDLE, PIO_APC_ROUTINE,
- PVOID, PIO_STATUS_BLOCK, PVOID,
- ULONG, FILE_INFORMATION_CLASS,
- ULONG, PUNICODE_STRING);
-
-using NtQueryFullAttributesFile_type =
- NTSTATUS(WINAPI*)(POBJECT_ATTRIBUTES, PFILE_NETWORK_OPEN_INFORMATION);
-using NtQueryAttributesFile_type = NTSTATUS(WINAPI*)(POBJECT_ATTRIBUTES,
- PFILE_BASIC_INFORMATION);
-
-using NtQueryObject_type = NTSTATUS(WINAPI*)(
- HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass,
- PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength);
-using NtQueryInformationFile_type = NTSTATUS(WINAPI*)(
- HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
- ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);
-
-using NtQueryInformationByName_type = NTSTATUS(WINAPI*)(
- HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
- ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);
-
-using NtOpenFile_type = NTSTATUS(WINAPI*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
- PIO_STATUS_BLOCK, ULONG, ULONG);
-using NtCreateFile_type = NTSTATUS(WINAPI*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
- PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG,
- ULONG, ULONG, ULONG, PVOID, ULONG);
-
-using NtClose_type = NTSTATUS(WINAPI*)(HANDLE);
-
-using NtTerminateProcess_type = NTSTATUS(WINAPI*)(HANDLE ProcessHandle,
- NTSTATUS ExitStatus);
-
-// Rtl
-
-using RtlDoesFileExists_U_type = NTSYSAPI BOOLEAN(NTAPI*)(PCWSTR);
-using RtlDosPathNameToRelativeNtPathName_U_WithStatus_type =
- NTSTATUS(NTAPI*)(PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePath,
- PRTL_RELATIVE_NAME RelativeName);
-using RtlReleaseRelativeName_type = void(NTAPI*)(PRTL_RELATIVE_NAME RelativeName);
-using RtlGetVersion_type = NTSTATUS(NTAPI*)(PRTL_OSVERSIONINFOW);
-
-extern NtQueryDirectoryFile_type NtQueryDirectoryFile;
-extern NtQueryDirectoryFileEx_type NtQueryDirectoryFileEx;
-extern NtQueryFullAttributesFile_type NtQueryFullAttributesFile;
-extern NtQueryAttributesFile_type NtQueryAttributesFile;
-extern NtQueryObject_type NtQueryObject;
-extern NtQueryInformationFile_type NtQueryInformationFile;
-extern NtQueryInformationByName_type NtQueryInformationByName;
-extern NtOpenFile_type NtOpenFile;
-extern NtCreateFile_type NtCreateFile;
-extern NtClose_type NtClose;
-extern NtTerminateProcess_type NtTerminateProcess;
-extern RtlDoesFileExists_U_type RtlDoesFileExists_U;
-extern RtlDosPathNameToRelativeNtPathName_U_WithStatus_type
- RtlDosPathNameToRelativeNtPathName_U_WithStatus;
-extern RtlReleaseRelativeName_type RtlReleaseRelativeName;
-extern RtlGetVersion_type RtlGetVersion;
-
-// ensures ntdll functions have been initialized (only needed during static objects
-// initialization)
-void ntdll_declarations_init();
-
-#pragma warning(pop)
diff --git a/libs/usvfs/src/shared/pch.cpp b/libs/usvfs/src/shared/pch.cpp
deleted file mode 100644
index 1d9f38c..0000000
--- a/libs/usvfs/src/shared/pch.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "pch.h"
diff --git a/libs/usvfs/src/shared/pch.h b/libs/usvfs/src/shared/pch.h
deleted file mode 100644
index e787f68..0000000
--- a/libs/usvfs/src/shared/pch.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#pragma once
-
-#include <algorithm>
-#include <atomic>
-#include <bitset>
-#include <cassert>
-#include <codecvt>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <ctime>
-#include <exception>
-#include <format>
-#include <functional>
-#include <future>
-#include <iomanip>
-#include <ios>
-#include <limits>
-#include <map>
-#include <memory>
-#include <mutex>
-#include <regex>
-#include <shared_mutex>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <thread>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-// clang-format off
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-#include <ShlObj.h>
-#include <comutil.h>
-#include <Psapi.h>
-#include <shellapi.h>
-#include <shlwapi.h>
-#include <fileapi.h>
-#include <DbgHelp.h>
-// clang-format on
-
-#define BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI 1
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/case_conv.hpp>
-#include <boost/any.hpp>
-#include <boost/container/flat_set.hpp>
-#include <boost/container/map.hpp>
-#include <boost/container/scoped_allocator.hpp>
-#include <boost/container/slist.hpp>
-#include <boost/container/string.hpp>
-#include <boost/container/vector.hpp>
-#include <boost/current_function.hpp>
-#include <boost/dll/runtime_symbol_info.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/format.hpp>
-#include <boost/interprocess/ipc/message_queue.hpp>
-#include <boost/interprocess/managed_windows_shared_memory.hpp>
-#include <boost/interprocess/offset_ptr.hpp>
-#include <boost/interprocess/smart_ptr/deleter.hpp>
-#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
-#include <boost/interprocess/smart_ptr/weak_ptr.hpp>
-#include <boost/interprocess/sync/named_mutex.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/locale.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/multi_index/ordered_index.hpp>
-#include <boost/multi_index_container.hpp>
-#include <boost/predef.h>
-#include <boost/static_assert.hpp>
-#include <boost/thread.hpp>
-#include <boost/thread/shared_lock_guard.hpp>
-#include <boost/thread/shared_mutex.hpp>
-#include <boost/tokenizer.hpp>
-#include <boost/type_traits.hpp>
-
-#include <spdlog/spdlog.h>
-
-namespace fs = boost::filesystem;
diff --git a/libs/usvfs/src/shared/shared_memory.h b/libs/usvfs/src/shared/shared_memory.h
deleted file mode 100644
index 28da34b..0000000
--- a/libs/usvfs/src/shared/shared_memory.h
+++ /dev/null
@@ -1,53 +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 bi = boost::interprocess;
-namespace bc = boost::container;
-
-namespace usvfs::shared
-{
-
-template <typename T>
-using OffsetPtrT = bi::offset_ptr<T, std::int32_t, std::uint64_t>;
-
-using VoidPointerT = OffsetPtrT<void>;
-
-// important: the windows shared memory mechanism, unlike other implementations,
-// automatically removes the SHM object when there are no more "subscribers".
-// MO currently depends on that feature!
-
-// managed_windows_shared_memory apparently doesn't support sharing between
-// 64bit and 32bit processes
-using managed_windows_shared_memory = bi::basic_managed_windows_shared_memory<
- char, bi::rbtree_best_fit<bi::mutex_family, VoidPointerT, 8>, bi::iset_index>;
-
-using SharedMemoryT = managed_windows_shared_memory;
-using SegmentManagerT = SharedMemoryT::segment_manager;
-
-using VoidAllocatorT = boost::container::scoped_allocator_adaptor<
- boost::interprocess::allocator<void, SegmentManagerT>>;
-using CharAllocatorT = VoidAllocatorT::rebind<char>::other;
-
-using StringT = bc::basic_string<char, std::char_traits<char>, CharAllocatorT>;
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/shmlogger.cpp b/libs/usvfs/src/shared/shmlogger.cpp
deleted file mode 100644
index 66512de..0000000
--- a/libs/usvfs/src/shared/shmlogger.cpp
+++ /dev/null
@@ -1,205 +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 warning(disable : 4714)
-#pragma warning(disable : 4503)
-#pragma warning(push, 3)
-#include "shmlogger.h"
-#pragma warning(pop)
-
-#pragma warning(disable : 4996)
-
-using namespace boost::interprocess;
-using namespace boost::posix_time;
-
-SHMLogger* SHMLogger::s_Instance = nullptr;
-
-SHMLogger::owner_t SHMLogger::owner;
-SHMLogger::client_t SHMLogger::client;
-
-SHMLogger::SHMLogger(owner_t, const std::string& queueName)
- : m_QueueName(queueName),
- m_LogQueue(create_only, queueName.c_str(), MESSAGE_COUNT, MESSAGE_SIZE),
- m_DroppedMessages(0)
-{
- if (s_Instance != nullptr) {
- throw std::runtime_error("duplicate shm logger instantiation");
- } else {
- s_Instance = this;
- }
-}
-
-SHMLogger::SHMLogger(client_t, const std::string& queueName)
- : m_QueueName(queueName), m_LogQueue(open_only, queueName.c_str()),
- m_DroppedMessages(0)
-{
- if (s_Instance != nullptr) {
- throw std::runtime_error("duplicate shm logger instantiation");
- } else {
- s_Instance = this;
- }
-}
-
-SHMLogger::~SHMLogger()
-{
- s_Instance = nullptr;
- message_queue_interop::remove(m_QueueName.c_str());
-}
-
-SHMLogger& SHMLogger::create(const char* instanceName)
-{
- if (s_Instance != nullptr) {
- throw std::runtime_error("duplicate shm logger instantiation");
- } else {
- std::string queueName = std::string("__shm_sink_") + instanceName;
- message_queue::remove(queueName.c_str());
- new SHMLogger(owner, queueName);
- atexit([]() {
- delete s_Instance;
- });
- }
- return *s_Instance;
-}
-
-SHMLogger& SHMLogger::open(const char* instanceName)
-{
- if (s_Instance != nullptr) {
- throw std::runtime_error("duplicate shm logger instantiation");
- } else {
- new SHMLogger(client, std::string("__shm_sink_") + instanceName);
- }
- return *s_Instance;
-}
-
-void SHMLogger::free()
-{
- if (s_Instance != nullptr) {
- SHMLogger* temp = s_Instance;
- s_Instance = nullptr;
- delete temp;
- }
-}
-
-bool SHMLogger::tryGet(char* buffer, size_t bufferSize)
-{
- message_queue_interop::size_type receivedSize;
- unsigned int prio;
- bool res = m_LogQueue.try_receive(buffer, static_cast<unsigned int>(bufferSize),
- receivedSize, prio);
- if (res) {
- buffer[std::min(bufferSize - 1, static_cast<size_t>(receivedSize))] = '\0';
- }
- return res;
-}
-
-void SHMLogger::get(char* buffer, size_t bufferSize)
-{
- message_queue_interop::size_type receivedSize;
- unsigned int prio;
- m_LogQueue.receive(buffer, static_cast<unsigned int>(bufferSize), receivedSize, prio);
- buffer[std::min(bufferSize - 1, static_cast<size_t>(receivedSize))] = '\0';
-}
-
-usvfs::sinks::shm_sink::shm_sink(const char* queueName)
- : m_LogQueue(open_only, (std::string("__shm_sink_") + queueName).c_str()),
- m_DroppedMessages(0)
-{}
-
-void usvfs::sinks::shm_sink::flush_() {}
-
-void usvfs::sinks::shm_sink::sink_it_(const spdlog::details::log_msg& msg)
-{
- int droppedMessages = m_DroppedMessages.load(std::memory_order_relaxed);
- if (droppedMessages > 0) {
- auto dropMessage = std::format("{} debug messages dropped", droppedMessages);
- if (m_LogQueue.try_send(dropMessage.c_str(),
- static_cast<unsigned int>(dropMessage.size()), 0)) {
- m_DroppedMessages.store(0, std::memory_order_relaxed);
- }
- }
-
- std::string message{msg.payload};
-
- // blacklist %USERNAME% because PII
- static const std::string username = std::string(getenv("USERNAME"));
- boost::algorithm::ireplace_all(message, std::string("\\") + username, "\\USERNAME");
- boost::algorithm::ireplace_all(message, std::string("/") + username, "/USERNAME");
-
- if (message.length() > SHMLogger::MESSAGE_SIZE) {
- std::vector<std::string> splitVec;
- boost::split(splitVec, message, boost::is_any_of("\r\n"), boost::token_compress_on);
- for (const std::string& line : splitVec) {
- output(msg.level, line);
- }
- } else {
- output(msg.level, message);
- }
-}
-
-void usvfs::sinks::shm_sink::output(spdlog::level::level_enum lev,
- const std::string& message)
-{
- bool sent = true;
-
- // spdlog auto-append line breaks which we don't need. Probably would be
- // better to not write the breaks to begin with?
- size_t count = std::min(message.find_last_not_of("\r\n") + 1,
- static_cast<size_t>(m_LogQueue.get_max_msg_size()));
-
- // depending on the log level, drop less important messages if the receiver
- // can't keep up
- switch (lev) {
- case spdlog::level::trace:
- case spdlog::level::debug:
- case spdlog::level::info: {
- // m_LogQueue.send(message.c_str(), count, 0);
- sent = m_LogQueue.try_send(message.c_str(), static_cast<unsigned int>(count), 0);
- } break;
- case spdlog::level::err:
- case spdlog::level::critical: {
- m_LogQueue.send(message.c_str(), static_cast<unsigned int>(count), 0);
- } break;
- default: {
- boost::posix_time::ptime time =
- microsec_clock::universal_time() + boost::posix_time::milliseconds(200);
- sent = m_LogQueue.timed_send(message.c_str(), static_cast<unsigned int>(count), 0,
- time);
- } break;
- }
-
- if (!sent) {
- m_DroppedMessages.fetch_add(1, std::memory_order_relaxed);
- }
-}
-
-void __cdecl boost::interprocess::ipcdetail::get_shared_dir(std::string& shared_dir)
-{
- PWSTR path;
- if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &path))) {
- _bstr_t bPath(path);
- shared_dir = (char*)bPath;
- shared_dir += "\\USVFS";
- } else {
- shared_dir = "C:\\ProgramData\\USVFS";
- }
- boost::filesystem::path boostPath(shared_dir);
- if (!boost::filesystem::exists(boostPath))
- boost::filesystem::create_directories(boostPath);
-}
diff --git a/libs/usvfs/src/shared/shmlogger.h b/libs/usvfs/src/shared/shmlogger.h
deleted file mode 100644
index 158f941..0000000
--- a/libs/usvfs/src/shared/shmlogger.h
+++ /dev/null
@@ -1,105 +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 <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-
-#include "logging.h"
-#include "shared_memory.h"
-#include "windows_sane.h"
-
-typedef boost::interprocess::message_queue_t<usvfs::shared::VoidPointerT>
- message_queue_interop;
-
-namespace usvfs::sinks
-{
-class shm_sink : public spdlog::sinks::base_sink<spdlog::details::null_mutex>
-{
-public:
- shm_sink(const char* queueName);
-
-protected:
- void sink_it_(const spdlog::details::log_msg& msg) override;
- void output(spdlog::level::level_enum lev, const std::string& message);
- void flush_() override;
-
-private:
- message_queue_interop m_LogQueue;
- std::atomic<int> m_DroppedMessages;
-};
-
-} // namespace usvfs::sinks
-
-class SHMLogger
-{
-public:
- static const size_t MESSAGE_COUNT = 1024;
- static const size_t MESSAGE_SIZE = 512;
-
- static SHMLogger& create(const char* instanceName);
- static SHMLogger& open(const char* instanceName);
- static void free();
-
- static bool isInstantiated() { return s_Instance != nullptr; }
-
- static inline SHMLogger& instance()
- {
- if (s_Instance == nullptr) {
- throw std::runtime_error("shm logger not instantiated");
- }
-
- return *s_Instance;
- }
-
- void log(LogLevel logLevel, const std::string& message);
- bool tryGet(char* buffer, size_t bufferSize);
- void get(char* buffer, size_t bufferSize);
-
-private:
- struct owner_t
- {};
- static owner_t owner;
-
- struct client_t
- {};
- static client_t client;
-
-private:
- SHMLogger(owner_t, const std::string& instanceName);
- SHMLogger(client_t, const std::string& instanceName);
-
- SHMLogger(const SHMLogger&) = delete;
- SHMLogger& operator=(const SHMLogger&) = delete;
-
- ~SHMLogger();
-
-private:
- static SHMLogger* s_Instance;
-
- message_queue_interop m_LogQueue;
-
- std::string m_SHMName;
- std::string m_LockName;
- std::string m_QueueName;
-
- std::atomic<int> m_DroppedMessages;
-};
diff --git a/libs/usvfs/src/shared/stringcast.cpp b/libs/usvfs/src/shared/stringcast.cpp
deleted file mode 100644
index 47822d5..0000000
--- a/libs/usvfs/src/shared/stringcast.cpp
+++ /dev/null
@@ -1,40 +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::shared
-{
-
-UINT windowsCP(CodePage codePage)
-{
- switch (codePage) {
- case CodePage::LOCAL:
- return CP_ACP;
- case CodePage::UTF8:
- return CP_UTF8;
- case CodePage::LATIN1:
- return 850;
- }
- // this should not be possible in practice
- throw std::runtime_error("unsupported codePage");
-}
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/stringcast.h b/libs/usvfs/src/shared/stringcast.h
deleted file mode 100644
index 0ce56d1..0000000
--- a/libs/usvfs/src/shared/stringcast.h
+++ /dev/null
@@ -1,170 +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 "exceptionex.h"
-
-namespace usvfs::shared
-{
-
-enum class CodePage
-{
- LOCAL,
- LATIN1,
- UTF8
-};
-
-template <typename ToT, typename FromT>
-class string_cast_impl
-{
-public:
- static ToT cast(const FromT& source, CodePage codePage, size_t sourceLength);
-};
-
-template <typename ToT, typename FromT>
-ToT string_cast(FromT source, CodePage codePage = CodePage::LOCAL,
- size_t sourceLength = std::numeric_limits<size_t>::max())
-{
- return string_cast_impl<ToT, FromT>::cast(source, codePage, sourceLength);
-}
-
-template <typename ToT, typename CharT>
-class string_cast_impl<ToT, std::basic_string<CharT>>
-{
-public:
- static ToT cast(const std::basic_string<CharT>& source, CodePage codePage,
- size_t sourceLength)
- {
- return string_cast_impl<ToT, const CharT*>::cast(source.c_str(), codePage,
- sourceLength);
- }
-};
-
-template <typename ToT, typename CharT>
-class string_cast_impl<ToT, CharT*>
-{
- BOOST_STATIC_ASSERT(!boost::is_base_and_derived<ToT, CharT>::value);
-
-public:
- static ToT cast(CharT* source, CodePage codePage, size_t sourceLength)
- {
- return string_cast_impl<ToT, const CharT*>::cast(source, codePage, sourceLength);
- }
-};
-
-template <typename ToT, typename CharT, int N>
-class string_cast_impl<ToT, CharT[N]>
-{
-public:
- static ToT cast(CharT (&source)[N], CodePage codePage, size_t sourceLength)
- {
- return string_cast_impl<ToT, const CharT*>::cast(source, codePage, sourceLength);
- }
-};
-
-UINT windowsCP(CodePage codePage);
-
-template <>
-class string_cast_impl<std::string, const wchar_t*>
-{
-public:
- static std::string cast(const wchar_t* const& source, CodePage codePage,
- size_t sourceLength)
- {
- std::string result;
-
- if (sourceLength == std::numeric_limits<size_t>::max()) {
- sourceLength = wcslen(source);
- }
-
- if (sourceLength > 0) {
- // use utf8 or local 8-bit encoding depending on user choice
- UINT cp = windowsCP(codePage);
- // preflight to find out the required buffer size
- int outLength = WideCharToMultiByte(cp, 0, source, static_cast<int>(sourceLength),
- nullptr, 0, nullptr, nullptr);
- if (outLength == 0) {
- throw windows_error("string conversion failed");
- }
- result.resize(outLength);
- outLength = WideCharToMultiByte(cp, 0, source, static_cast<int>(sourceLength),
- &result[0], outLength, nullptr, nullptr);
- if (outLength == 0) {
- throw windows_error("string conversion failed");
- }
- // fix output string length (i.e. in case of unconvertible characters
- while (result[outLength - 1] == L'\0') {
- result.resize(--outLength);
- }
- }
-
- return result;
- }
-};
-
-template <>
-class string_cast_impl<std::wstring, const char*>
-{
-public:
- static std::wstring cast(const char* const& source, CodePage codePage,
- size_t sourceLength)
- {
- std::wstring result;
-
- if (sourceLength == std::numeric_limits<size_t>::max()) {
- sourceLength = strlen(source);
- }
-
- if (sourceLength > 0) {
- // use utf8 or local 8-bit encoding depending on user choice
- UINT cp = windowsCP(codePage);
- // preflight to find out the required source size
- int outLength = MultiByteToWideChar(cp, 0, source, static_cast<int>(sourceLength),
- &result[0], 0);
- if (outLength == 0) {
- throw windows_error("string conversion failed");
- }
- result.resize(outLength);
- outLength = MultiByteToWideChar(cp, 0, source, static_cast<int>(sourceLength),
- &result[0], outLength);
- if (outLength == 0) {
- throw windows_error("string conversion failed");
- }
- while (result[outLength - 1] == L'\0') {
- result.resize(--outLength);
- }
- }
-
- return result;
- }
-};
-
-template <>
-class string_cast_impl<std::wstring, const wchar_t*>
-{
-public:
- static std::wstring cast(const wchar_t* const& source, CodePage, size_t)
- {
- return std::wstring(source);
- }
-};
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/stringutils.cpp b/libs/usvfs/src/shared/stringutils.cpp
deleted file mode 100644
index 2ddbf64..0000000
--- a/libs/usvfs/src/shared/stringutils.cpp
+++ /dev/null
@@ -1,146 +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 "stringutils.h"
-#include "windows_sane.h"
-
-namespace usvfs::shared
-{
-
-void strncpy_sz(char* dest, const char* src, size_t destSize)
-{
- if (destSize > 0) {
- strncpy(dest, src, destSize - 1);
- dest[destSize - 1] = '\0';
- }
-}
-
-void wcsncpy_sz(wchar_t* dest, const wchar_t* src, size_t destSize)
-{
- if ((destSize > 0) && (dest != nullptr)) {
- wcsncpy(dest, src, destSize - 1);
- dest[destSize - 1] = L'\0';
- }
-}
-
-bool startswith(const wchar_t* string, const wchar_t* subString)
-{
- while ((*string != '\0') && (*subString != '\0')) {
- if (towlower(*string) != towlower(*subString)) {
- return false;
- }
- ++string;
- ++subString;
- }
-
- return *subString == '\0';
-}
-
-static fs::path normalize(const fs::path& path)
-{
- fs::path result;
-
- boost::locale::generator gen;
- auto loc = gen("en_US.UTF-8");
- for (fs::path::iterator iter = path.begin(); iter != path.end(); ++iter) {
- if (*iter == "..") {
- result = result.parent_path();
- } else if (*iter != ".") {
- result /= boost::to_lower_copy(iter->string(), loc);
- } // single dot is ignored
- }
- return result;
-}
-
-fs::path make_relative(const fs::path& fromIn, const fs::path& toIn)
-{
- // converting path to lower case to make iterator comparison work correctly
- // on case-insenstive filesystems
- fs::path from(fs::absolute(fromIn));
- fs::path to(fs::absolute(toIn));
-
- // find common base
- fs::path::const_iterator fromIter(from.begin());
- fs::path::const_iterator toIter(to.begin());
-
- // TODO the following equivalent test is probably quite expensive as new
- // paths are created for each iteration but the case sensitivity depends on
- // the fs
- while ((fromIter != from.end()) && (toIter != to.end()) &&
- (boost::iequals(fromIter->string(), toIter->string()))) {
- ++fromIter;
- ++toIter;
- }
-
- // Navigate backwards in directory to reach previously found base
- fs::path result;
- for (; fromIter != from.end(); ++fromIter) {
- if (*fromIter != ".") {
- result /= "..";
- }
- }
-
- // Now navigate down the directory branch
- for (; toIter != to.end(); ++toIter) {
- result /= *toIter;
- }
- return result;
-}
-
-std::string to_hex(void* bufferIn, size_t bufferSize)
-{
- unsigned char* buffer = static_cast<unsigned char*>(bufferIn);
- std::ostringstream temp;
- temp << std::hex;
- for (size_t i = 0; i < bufferSize; ++i) {
- temp << std::setfill('0') << std::setw(2) << (unsigned int)buffer[i];
- if ((i % 16) == 15) {
- temp << "\n";
- } else {
- temp << " ";
- }
- }
- return temp.str();
-}
-
-std::wstring to_upper(const std::wstring& input)
-{
- std::wstring result;
- result.resize(input.size());
- ::LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, input.c_str(),
- static_cast<int>(input.size()), &result[0],
- static_cast<int>(result.size()));
- return result;
-}
-
-std::string byte_string(std::size_t n)
-{
- auto s = std::to_string(n);
- auto p = s.size();
-
- while (p > 3) {
- p -= 3;
- s.insert(p, 1, ',');
- }
-
- return s + " B";
-}
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/stringutils.h b/libs/usvfs/src/shared/stringutils.h
deleted file mode 100644
index 64d0af7..0000000
--- a/libs/usvfs/src/shared/stringutils.h
+++ /dev/null
@@ -1,57 +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::shared
-{
-
-void strncpy_sz(char* dest, const char* src, size_t destSize);
-void wcsncpy_sz(wchar_t* dest, const wchar_t* src, size_t destSize);
-
-bool startswith(const wchar_t* string, const wchar_t* subString);
-
-// Return path when appended to a_From will resolve to same as a_To
-fs::path make_relative(const fs::path& from, const fs::path& to);
-
-std::string to_hex(void* bufferIn, size_t bufferSize);
-
-// convert unicode string to upper-case (locale invariant)
-std::wstring to_upper(const std::wstring& input);
-
-// formats a number with thousand separators and B at the end
-//
-std::string byte_string(std::size_t n);
-
-class FormatGuard
-{
- std::ostream& m_Stream;
- std::ios::fmtflags m_Flags;
-
-public:
- FormatGuard(std::ostream& stream) : m_Stream(stream), m_Flags(stream.flags()) {}
-
- ~FormatGuard() { m_Stream.flags(m_Flags); }
-
- FormatGuard(const FormatGuard&) = delete;
- FormatGuard& operator=(FormatGuard&) = delete;
-};
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/tree_container.h b/libs/usvfs/src/shared/tree_container.h
deleted file mode 100644
index a2f61fa..0000000
--- a/libs/usvfs/src/shared/tree_container.h
+++ /dev/null
@@ -1,630 +0,0 @@
-#pragma once
-
-#include "directory_tree.h"
-#include "shared_memory.h"
-
-namespace usvfs::shared
-{
-
-// smart pointer to DirectoryTrees (only intended for top-level nodes). This
-// will transparently switch to new shared memory regions in case they get
-// reallocated
-//
-//
-// reassign() is called from a variety of places when the current chunk of
-// shared memory is full or is marked as being outdated; its job is to either
-// find another chunk that may have been created by another process or to create
-// a brand new one
-//
-//
-// if there is only a single process hooked, it will slowly fill up the shared
-// memory when adding files, eventually throw a bi::bad_alloc and end up in
-// reassign(); a new block will be allocated, the data copied over, and the old
-// block will be deallocated
-//
-//
-// when multiple processes are involved, things are more complicated
-//
-// two processes A and B will start by using the same shared memory, but they
-// have their own pointer to it that's local to the process (the `m_TreeMeta`
-// member variable)
-//
-// so when process A fills up the shared memory and reallocates it, process B
-// is still pointing to the old shared memory; only when process B does some
-// operation that accesses the file tree will the pointer be checked and
-// adjusted to point to the new shared memory
-//
-// in this example, when process A ran out of memory, it set `outdated` to
-// `true` in the shared memory block, allocated a new one and copied the data
-// over, but it did not deallocate the block because process B is still
-// pointing to it
-//
-// when process B tries to access the block, it checks `outdated` (see get());
-// if it's true, it means that it's pointing to an outdated shared memory block
-// and must find the new one that process A created
-//
-// the new block is not necessarily the very next name, it is possible for
-// process A to burn through a series of blocks quickly when adding a bunch of
-// new files, and these blocks will all have been deallocated by the time
-// process B tries to find the newest one
-//
-// so when a process sees its block as outdated, it will try to open a bunch of
-// names until one exists that is not outdated; if it can't find the block
-// (shouldn't happen), it will just create a new one
-//
-template <typename TreeT>
-class TreeContainer
-{
-public:
- /**
- * @brief Constructor
- * @param SHMName name of the shared memory holding the tree. This should contain the
- * running number
- * @param size initial size in bytes of the container. since the tree is resized by
- * doubling this should be a power of two. 64k is supposed to be the page size on
- * windows so smaller allocations make little sense
- * @note size can't be too small. If initial allocations fail automatic growing won't
- * work
- */
- TreeContainer(const std::string& SHMName, size_t size = 64 * 1024)
- : m_TreeMeta(nullptr), m_SHMName(SHMName)
- {
- std::locale global_loc = std::locale();
- std::locale loc(global_loc, new fs::detail::utf8_codecvt_facet);
- fs::path::imbue(loc);
-
- // append _1 to the name if it doesn't end with _N already
- std::regex pattern(R"exp((.*_)(\d+))exp");
- std::smatch match;
- std::string shmName(m_SHMName.c_str());
- regex_match(shmName, match, pattern);
-
- if (match.size() != 3) {
- m_SHMName += "_1";
- }
-
- // creates a new memory block if this is the first process to run or attach
- // to an already existing one
- createOrOpen(m_SHMName, size);
-
- spdlog::get("usvfs")->info("attached to {0} with {1} nodes, size {2}", m_SHMName,
- m_TreeMeta->tree->numNodesRecursive(),
- byte_string(m_SHM->get_size()));
- }
-
- TreeContainer(const TreeContainer&) = delete;
- TreeContainer& operator=(const TreeContainer&) = delete;
-
- ~TreeContainer()
- {
- if (unassign(m_SHM, m_TreeMeta)) {
- bi::shared_memory_object::remove(m_SHMName.c_str());
- }
- }
-
- /**
- * @return retrieve an allocator that can be used to create objects in this tree
- */
- VoidAllocatorT allocator() { return VoidAllocatorT(m_SHM->get_segment_manager()); }
-
- template <typename... Arguments>
- typename TreeT::DataT create(Arguments&&... args)
- {
- return TreeT::DataT(std::forward<Arguments>(args)..., allocator());
- }
-
- TreeT* operator->() { return get(); }
-
- /**
- * @return raw pointer to the managed tree
- */
- TreeT* get()
- {
- if (m_TreeMeta->outdated) {
- reassign();
- }
-
- return m_TreeMeta->tree.get();
- }
-
- /**
- * @return raw const pointer to the managed tree
- */
- const TreeT* get() const
- {
- if (m_TreeMeta->outdated) {
- // safe const_cast, TreeContainer are never created const
- const_cast<TreeContainer<TreeT>*>(this)->reassign();
- }
-
- return m_TreeMeta->tree.get();
- }
-
- const TreeT* operator->() const { return get(); }
-
- /**
- * @return current name of the managed shared memory
- */
- std::string shmName() const { return m_SHMName; }
-
- void clear() { m_TreeMeta->tree->clear(); }
-
- /**
- * @brief add a new file to the tree
- *
- * @param name name of the file, expected to be relative to this directory
- * @param data the file data to attach
- * @param flags flags for this files
- * @param overwrite if true, the new leaf will overwrite an existing one that compares
- *as "equal"
- * @return pointer to the new node or a null ptr
- **/
- template <typename T>
- typename TreeT::NodePtrT addFile(const fs::path& name, const T& data,
- TreeFlags flags = 0, bool overwrite = true)
- {
- for (;;) {
- DecomposablePath dp(name.string());
-
- try {
- return addNode(m_TreeMeta->tree.get(), dp, data, overwrite, flags, allocator());
- } catch (const bi::bad_alloc&) {
- }
-
- reassign();
- }
- }
-
- /**
- * @brief add a new directory to the tree
- *
- * @param name name of the file, expected to be relative to this directory
- * @param data the file data to attach
- * @param flags flags for this files
- * @param overwrite if true, the new leaf will overwrite an existing one that compares
- *as "equal"
- * @return pointer to the new node or a null ptr
- **/
- template <typename T>
- typename TreeT::NodePtrT addDirectory(const fs::path& name, const T& data,
- TreeFlags flags = 0, bool overwrite = true)
- {
- for (;;) {
- DecomposablePath dp(name.string());
-
- try {
- return addNode(m_TreeMeta->tree.get(), dp, data, overwrite,
- flags | FLAG_DIRECTORY, allocator());
- } catch (const bi::bad_alloc&) {
- }
-
- reassign();
- }
- }
-
- void getBuffer(void*& buffer, size_t& bufferSize) const
- {
- buffer = m_SHM->get_address();
- bufferSize = m_SHM->get_size();
- }
-
-private:
- struct TreeMeta
- {
- TreeMeta(const typename TreeT::DataT& data, SegmentManagerT* segmentManager)
- : tree(segmentManager->construct<TreeT>(bi::anonymous_instance)(
- "", true, TreeT::NodePtrT(), data, VoidAllocatorT(segmentManager))),
- referenceCount(0), // reference count only set on top level node
- outdated(false)
- {}
-
- OffsetPtrT<TreeT> tree;
- long referenceCount;
- bool outdated;
- bi::interprocess_mutex mutex;
- };
-
- std::string m_SHMName;
- std::shared_ptr<SharedMemoryT> m_SHM;
- TreeMeta* m_TreeMeta;
-
- typename TreeT::DataT createEmpty()
- {
- return createDataEmpty<typename TreeT::DataT>(allocator());
- }
-
- template <typename T>
- TreeT* createSubNode(const VoidAllocatorT& allocator, std::string_view name,
- unsigned long flags, const T& data)
- {
- auto* manager = allocator.get_segment_manager();
-
- return manager->construct<TreeT>(bi::anonymous_instance)(
- name, flags, TreeT::NodePtrT(),
- createData<typename TreeT::DataT, T>(data, allocator), manager);
- }
-
- typename TreeT::NodePtrT createSubPtr(TreeT* subNode)
- {
- SharedMemoryT::segment_manager* manager = m_SHM->get_segment_manager();
- return TreeT::NodePtrT(subNode, allocator(), TreeT::DeleterT(manager));
- }
-
- template <typename T>
- typename TreeT::NodePtrT addNode(TreeT* base, DecomposablePath& path, const T& data,
- bool overwrite, unsigned int flags,
- const VoidAllocatorT& allocator)
- {
- if (!path.peekNext()) {
- typename TreeT::NodePtrT newNode = base->node(path.current());
-
- if (!newNode) {
- // last name component, should be the filename
- TreeT* node = createSubNode(allocator, path.current(), flags, data);
- newNode = createSubPtr(node);
- newNode->m_Self = TreeT::WeakPtrT(newNode);
- newNode->m_Parent = base->m_Self;
- base->set(StringT(path.current(), allocator), newNode);
- return newNode;
- } else if (overwrite) {
- newNode->m_Data = createData<typename TreeT::DataT, T>(data, allocator);
- newNode->m_Flags = static_cast<usvfs::shared::TreeFlags>(flags);
- return newNode;
- } else {
- // the node is already in the tree, overwrite is false, nothing to do
- return {};
- }
- } else {
- // not last component, continue search in child node
- auto subNode = base->m_Nodes.find(path.current());
-
- if (subNode == base->m_Nodes.end()) {
- typename TreeT::NodePtrT newNode = createSubPtr(createSubNode(
- allocator, path.current(), FLAG_DIRECTORY | FLAG_DUMMY, createEmpty()));
-
- subNode =
- base->m_Nodes.emplace(StringT(path.current(), allocator), newNode).first;
- subNode->second->m_Self = TreeT::WeakPtrT(subNode->second);
- subNode->second->m_Parent = base->m_Self;
- }
-
- path.next();
-
- return addNode(subNode->second.get().get(), path, data, overwrite, flags,
- allocator);
- }
- }
-
- /**
- * @brief copy content of one tree to a different tree (in a different shared memory
- * segment
- * @param destination
- * @param reference
- * @note at the time this is called, destination needs to refer to the shm of
- * "destination" so that objects can be allocated in the new tree
- */
- void copyTree(TreeT* destination, const TreeT* reference)
- {
- VoidAllocatorT allocator = VoidAllocatorT(m_SHM->get_segment_manager());
- destination->m_Flags = reference->m_Flags;
- dataAssign(destination->m_Data, reference->m_Data);
- destination->m_Name.assign(reference->m_Name.c_str());
-
- for (const auto& kv : reference->m_Nodes) {
- TreeT* newNode = createSubNode(allocator, "", true, createEmpty());
- typename TreeT::NodePtrT newNodePtr = createSubPtr(newNode);
-
- // need to set self BEFORE recursively copying the subtree, otherwise
- // how would we assign parent pointers?
- newNode->m_Self = newNodePtr;
-
- TreeT* source = reinterpret_cast<TreeT*>(kv.second.get().get());
- copyTree(newNode, source);
- destination->set(newNode->m_Name, newNodePtr);
- newNode->m_Parent = destination->m_Self;
- }
- }
-
- int increaseRefCount(TreeMeta* treeMeta)
- {
- bi::scoped_lock<bi::interprocess_mutex> lock(treeMeta->mutex);
- return ++treeMeta->referenceCount;
- }
-
- int decreaseRefCount(TreeMeta* treeMeta)
- {
- bi::scoped_lock<bi::interprocess_mutex> lock(treeMeta->mutex);
- return --treeMeta->referenceCount;
- }
-
- // see activateSHM() for return value
- //
- std::optional<std::string> createOrOpen(const std::string& SHMName, size_t size)
- {
- SharedMemoryT* newSHM = openSHM(SHMName);
-
- if (newSHM) {
- spdlog::get("usvfs")->info("{} opened in process {}", SHMName,
- ::GetCurrentProcessId());
- } else {
- newSHM = createSHM(SHMName, size);
-
- if (newSHM) {
- spdlog::get("usvfs")->info("{} created in process {}", SHMName,
- ::GetCurrentProcessId());
- }
- }
-
- if (!newSHM) {
- spdlog::get("usvfs")->error("failed to create or open {} in process {}", SHMName,
- ::GetCurrentProcessId());
-
- throw std::exception("no shm instance");
- }
-
- return activateSHM(newSHM, SHMName);
- }
-
- // see activateSHM() for return value
- //
- SharedMemoryT* createSHM(const std::string& SHMName, size_t size)
- {
- try {
- return new SharedMemoryT(bi::create_only, SHMName.c_str(),
- static_cast<unsigned int>(size));
- } catch (const bi::interprocess_exception&) {
- }
-
- return nullptr;
- }
-
- // see activateSHM() for return value
- //
- SharedMemoryT* openSHM(const std::string& SHMName)
- {
- try {
- return new SharedMemoryT(bi::open_only, SHMName.c_str());
- } catch (const bi::interprocess_exception&) {
- }
-
- return nullptr;
- }
-
- // makes the given shm current, returns the name of the previous shm block
- // if it is now unused and must be destroyed; if the block is still used by
- // another process, returns empty
- //
- // see reassign()
- //
- std::optional<std::string> activateSHM(SharedMemoryT* shm, const std::string& SHMName)
- {
- std::shared_ptr<SharedMemoryT> oldSHM = m_SHM;
-
- m_SHM.reset(shm);
- std::pair<TreeMeta*, SharedMemoryT::size_type> res = m_SHM->find<TreeMeta>("Meta");
-
- if (res.first == nullptr) {
- res.first = m_SHM->construct<TreeMeta>("Meta")(createEmpty(),
- m_SHM->get_segment_manager());
- if (res.first == nullptr) {
- USVFS_THROW_EXCEPTION(bi::bad_alloc());
- }
- if (m_TreeMeta != nullptr) {
- copyTree(res.first->tree.get(), m_TreeMeta->tree.get());
- }
- }
-
- increaseRefCount(res.first);
-
- std::optional<std::string> deadSHMName;
-
- if (oldSHM.get() != nullptr) {
- const bool lastUser = unassign(oldSHM, m_TreeMeta);
- if (lastUser) {
- deadSHMName = m_SHMName;
- }
- }
-
- m_TreeMeta = res.first;
- m_SHMName = SHMName;
-
- return deadSHMName;
- }
-
- static std::string followupName(const std::string& currentName)
- {
- std::regex pattern(R"exp((.*_)(\d+))exp");
- std::smatch match;
- regex_match(currentName, match, pattern);
-
- if (match.size() != 3) {
- USVFS_THROW_EXCEPTION(usage_error() << ex_msg("shared memory name invalid"));
- }
-
- const int count = boost::lexical_cast<int>(match[2]);
- return match[1].str() + std::to_string(count + 1);
- }
-
- bool unassign(const std::shared_ptr<SharedMemoryT>& shm, TreeMeta* tree)
- {
- if (tree == nullptr) {
- return true;
- }
-
- if (decreaseRefCount(tree) == 0) {
- shm->get_segment_manager()->destroy_ptr(tree);
- return true;
- } else {
- return false;
- }
- }
-
- // re-entrancy: every time a process switches to a new shared memory block, it
- // will know whether it was the last process to have a handle to it; when that
- // happens, the block will be destroyed to avoid leaking it
- //
- // destroying these blocks is somewhat dangerous: it ends up in boost, which
- // will try to access the filesystem to see if the name of the shared memory
- // corresponds to a file on the drive, which can call hooked functions and end
- // up right back here
- //
- // (note that in usvfs, only the shared memory for the log file uses a real
- // file on the filesystem, see shmlogger.cpp; all the tree stuff uses
- // anonymous, memory mapped files that live in the Windows pagefile)
- //
- // so the old blocks can be destroyed, but only after all the shenanigans with
- // finding the correct shared memory block are over and `m_TreeMeta` points to
- // a valid block, so all the names of the dead shared memory blocks are kept
- // in a vector and deallocated at the very end
- //
- void reassign()
- {
- // list of all the shared memory blocks that are now unused and can be
- // destroyed
- std::vector<std::string> deadSHMNames;
-
- if (m_TreeMeta->outdated) {
- // this block was marked as outdated, which should only happen when
- // another process has ran out of memory and started allocating blocks
- // with higher numbers
-
- spdlog::get("usvfs")->info("tree {0} is outdated, looking for another one",
- m_SHMName);
-
- if (findNewerBlock(deadSHMNames)) {
- // the new block was found and activated
- return;
- }
- } else {
- // this block isn't outdated, so reassign() was called because a bad_alloc
- // exception was thrown
- spdlog::get("usvfs")->info(
- "ran out of memory in tree {0}, will create another one", m_SHMName);
- }
-
- // either the block is full or it's outdated, but no higher block was found;
- // just create a new one
-
- createNewBlock(deadSHMNames);
-
- // remove the old shared memory blocks; this can be recursive and call
- // reassign() again, but it's safe at this point
- for (const std::string& name : deadSHMNames) {
- spdlog::get("usvfs")->info("destroying {0}", name);
- bi::shared_memory_object::remove(name.c_str());
- }
- }
-
- // tries a series of shm names above the current one in the hope of finding
- // the most recent one
- //
- // if the new block was found and activated correctly, returns true
- //
- // when findNewerBlock() returns false, this container will either still be
- // pointing to the same, outdated block as it started with, or it might have
- // moved up to another outdated block, but it will always be pointing to
- // a valid block in memory
- //
- bool findNewerBlock(std::vector<std::string>& deadSHMNames)
- {
- // how many blocks are checked above the current one; it's unlikely there
- // will ever be more than 15 to 20 blocks allocated, but this is high
- // because it's really the only way to keep processes connected to the same
- // shm block, so processes must not fail to find the next one
- constexpr int Tries = 100;
-
- std::string nextName = m_SHMName;
-
- for (int i = 0; i < Tries; ++i) {
- // the shm name is something like "mod_organizer_3", which becomes
- // "mod_organizer_4"
- nextName = followupName(nextName);
- spdlog::get("usvfs")->info("opening {0}", nextName);
-
- // open the shm, see if it exists
- SharedMemoryT* shm = openSHM(nextName);
-
- if (!shm) {
- // this is not necessarily an error, another process might have created
- // a bunch of blocks and destroyed them before this process had a chance
- // to see them, so just keep going
- spdlog::get("usvfs")->info("{0} doesn't exist", nextName);
- continue;
- }
-
- spdlog::get("usvfs")->info("{0} exists, activating", nextName);
- const auto deadSHMName = activateSHM(shm, nextName);
-
- // if this process was the last user of the previous block, it must be
- // deallocated, but only after this whole thing is finished, because it
- // can end up calling reassign() again
- if (deadSHMName) {
- spdlog::get("usvfs")->info("will destroy {0}", *deadSHMName);
- deadSHMNames.push_back(*deadSHMName);
- }
-
- // another process might have already created this block, run out of
- // memory and created more, so make sure to only stop when finding a block
- // that's not outdated
- if (m_TreeMeta->outdated) {
- spdlog::get("usvfs")->info("{0} is also outdated", nextName);
- } else {
- // shm opened correctly, activated and not outdated, done
- spdlog::get("usvfs")->info("{0} not outdated, taking it, size now {1}",
- nextName, byte_string(m_SHM->get_size()));
-
- return true;
- }
- }
-
- // this block is outdated, but no other valid block was found above; this
- // shouldn't happen, but just create a new block to make sure programs can
- // still run
-
- spdlog::get("usvfs")->error(
- "found no existing tree above {0}, will create a new one", m_SHMName);
-
- return false;
- }
-
- // creates a new block and activates it, throws on failure
- //
- void createNewBlock(std::vector<std::string>& deadSHMNames)
- {
- // the current block is now considered stale, so make sure other processes
- // are aware of it and try to find the new block
- //
- // todo: there really should be some synchronization here, another process
- // can pick up this flag before the next block has been created
- m_TreeMeta->outdated = true;
-
- // the shm name is something like "mod_organizer_3", which becomes
- // "mod_organizer_4"
- const std::string nextName = followupName(m_SHMName);
- spdlog::get("usvfs")->info("creating {0}", nextName);
-
- SharedMemoryT* shm = createSHM(nextName, m_SHM->get_size() * 2);
-
- if (!shm) {
- // this shouldn't happen
- spdlog::get("usvfs")->error("failed to create {0}", nextName);
- throw std::exception("cannot create block");
- }
-
- spdlog::get("usvfs")->info("{0} created, activating", nextName);
- const auto deadSHMName = activateSHM(shm, nextName);
-
- // if this process was the last user of the previous block, it must be
- // deallocated, but only after this whole thing is finished, because it
- // can end up calling reassign() again
- if (deadSHMName) {
- spdlog::get("usvfs")->info("will destroy {0}", *deadSHMName);
- deadSHMNames.push_back(*deadSHMName);
- }
-
- spdlog::get("usvfs")->info("tree {0} size now {1}", m_SHMName,
- byte_string(m_SHM->get_size()));
- }
-};
-
-} // namespace usvfs::shared
diff --git a/libs/usvfs/src/shared/unicodestring.cpp b/libs/usvfs/src/shared/unicodestring.cpp
deleted file mode 100644
index f997e7f..0000000
--- a/libs/usvfs/src/shared/unicodestring.cpp
+++ /dev/null
@@ -1,77 +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 "unicodestring.h"
-#include "logging.h"
-#include "stringcast.h"
-#include "stringutils.h"
-
-namespace usvfs
-{
-
-UnicodeString::UnicodeString(LPCWSTR string, size_t length)
-{
- if (length == std::string::npos) {
- length = wcslen(string);
- }
- m_Buffer.resize(length + 1);
- memcpy(m_Buffer.data(), string, length * sizeof(wchar_t));
- update();
-}
-
-UnicodeString::UnicodeString(const std::wstring& string)
-{
- m_Buffer.resize(string.length() + 1);
- memcpy(m_Buffer.data(), string.data(), string.length() * sizeof(wchar_t));
- update();
-}
-
-UnicodeString& UnicodeString::operator=(const std::wstring& string)
-{
- m_Buffer.resize(string.length() + 1);
- memcpy(m_Buffer.data(), string.data(), string.length() * sizeof(wchar_t));
- update();
- return *this;
-}
-
-UnicodeString& UnicodeString::appendPath(PUNICODE_STRING path)
-{
- if (path != nullptr && path->Buffer && path->Length) {
- auto appendAt = size();
- if (appendAt) {
- m_Buffer.resize(m_Buffer.size() + path->Length / sizeof(WCHAR) + 1);
- m_Buffer[appendAt++] = L'\\';
- } else
- m_Buffer.resize(path->Length / sizeof(WCHAR) + 1);
- memcpy(&m_Buffer[appendAt], path->Buffer, path->Length);
- update();
- }
-
- return *this;
-}
-
-void UnicodeString::update()
-{
- m_Data.Length = static_cast<USHORT>(size() * sizeof(WCHAR));
- m_Data.MaximumLength = static_cast<USHORT>((m_Buffer.capacity() - 1) * sizeof(WCHAR));
- m_Data.Buffer = m_Buffer.data();
-}
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/shared/unicodestring.h b/libs/usvfs/src/shared/unicodestring.h
deleted file mode 100644
index 77071ad..0000000
--- a/libs/usvfs/src/shared/unicodestring.h
+++ /dev/null
@@ -1,110 +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 "ntdll_declarations.h"
-#include "windows_sane.h"
-
-#include "formatters.h"
-
-namespace usvfs
-{
-
-/**
- * @brief C++ wrapper for the windows UNICODE_STRING structure
- */
-class UnicodeString
-{
-public:
- UnicodeString() : m_Buffer(1) { update(); }
-
- UnicodeString(const UnicodeString& other) : m_Buffer(other.m_Buffer) { update(); }
-
- UnicodeString(UnicodeString&& other) : m_Buffer(std::move(other.m_Buffer))
- {
- update();
- }
-
- UnicodeString(const std::wstring& string);
- UnicodeString(LPCWSTR string, size_t length = std::string::npos);
-
- UnicodeString& operator=(const std::wstring& string);
-
- UnicodeString& operator=(const UnicodeString& other)
- {
- m_Buffer = other.m_Buffer;
- update();
- return *this;
- }
-
- UnicodeString& operator=(UnicodeString&& other)
- {
- m_Buffer = std::move(other.m_Buffer);
- update();
- return *this;
- }
-
- /**
- * @brief convert to a WinNt Api-style unicode string. This is only valid as long
- * as the string isn't modified
- */
- explicit operator PUNICODE_STRING() { return &m_Data; }
-
- /**
- * @brief convert to a Win32 Api-style unicode string. This is only valid as long
- * as the string isn't modified
- */
- explicit operator LPCWSTR() const { return m_Data.Buffer; }
-
- /**
- * @return length of the string in 16-bit words (not including zero termination)
- */
- size_t size() const { return m_Buffer.size() - 1; }
-
- wchar_t operator[](size_t pos) const { return m_Buffer[pos]; }
-
- UnicodeString& appendPath(PUNICODE_STRING path);
-
-private:
- friend struct ::std::formatter<UnicodeString, char>;
-
- void update();
-
- UNICODE_STRING m_Data;
- std::vector<wchar_t> m_Buffer;
-};
-
-} // namespace usvfs
-
-template <>
-struct std::formatter<usvfs::UnicodeString, char>
- : std::formatter<PCUNICODE_STRING, char>
-{
- template <class FmtContext>
- FmtContext::iterator format(const usvfs::UnicodeString& v, FmtContext& ctx) const
- {
- if (v.size() == 0) {
- return std::format_to(ctx.out(), "<empty string>");
- } else {
- return std::formatter<PCUNICODE_STRING, char>::format(&v.m_Data, ctx);
- }
- }
-};
diff --git a/libs/usvfs/src/shared/wildcard.cpp b/libs/usvfs/src/shared/wildcard.cpp
deleted file mode 100644
index a2efe6d..0000000
--- a/libs/usvfs/src/shared/wildcard.cpp
+++ /dev/null
@@ -1,194 +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 "wildcard.h"
-#include "logging.h"
-#include "windows_sane.h"
-
-static bool IsInnerMatch(LPCWSTR pszString, LPCWSTR pszMatch)
-{
- while (*pszMatch != L'\0') {
- if ((*pszMatch == L'?') || (*pszMatch == L'>')) {
- if (!*pszString) {
- // ? must match exactly one character
- return false;
- }
-
- ++pszString;
- ++pszMatch;
- } else if ((*pszMatch == L'*') || (*pszMatch == L'<')) {
- if (IsInnerMatch(pszString, pszMatch + 1)) {
- // * may match empty string or we may have matched something already
- return true;
- }
-
- return *pszString && IsInnerMatch(pszString + 1, pszMatch);
-
- // the rest of the string can't be matched
- } else {
- if (CharUpperW(MAKEINTRESOURCEW(MAKELONG(*pszString++, 0))) !=
- CharUpperW(MAKEINTRESOURCEW(MAKELONG(*pszMatch++, 0)))) {
- // regular chars compare
- return false;
- }
- }
- }
-
- return !*pszString && !*pszMatch;
-}
-
-static LPCSTR InnerMatch(LPCSTR pszString, LPCSTR pszMatch)
-{
- // We have a special case where string is empty ("") and the mask is "*".
- // We need to handle this too. So we can't test on !*pszString here.
- // The loop breaks when the match string is exhausted.
- while (*pszString != '\0') {
- // Single wildcard character
- if ((*pszMatch == '?') || (*pszMatch == '>')) {
- // can't match directory separator
- if ((*pszString == '\\') || (*pszString == '/'))
- return nullptr;
-
- // Matches any character except empty string
- if (*pszString == '\0') {
- // string consumed, part of the pattern is left
- return pszMatch;
- }
-
- // OK next
- ++pszString;
- ++pszMatch;
- } else if ((*pszMatch == '*') || (*pszMatch == '<')) {
- // * doesn't match directory separators
- if ((*pszString == '\\') || (*pszString == '/')) {
- ++pszMatch;
- continue;
- }
-
- // Need to do some tricks.
-
- // 1. The wildcard * is ignored.
- // So just an empty string matches. This is done by recursion.
- // Because we eat one character from the match string, the
- // recursion will stop.
- {
- LPCSTR remainder = InnerMatch(pszString, pszMatch + 1);
- if (remainder != nullptr) {
- // we have a match and the * replaces no other character
- return remainder;
- }
- }
-
- // 2. Chance we eat the next character and try it again, with a
- // wildcard * match. This is done by recursion. Because we eat
- // one character from the string, the recursion will stop.
- if (*pszString != '\0') {
- LPCSTR remainder = InnerMatch(pszString + 1, pszMatch);
- if (remainder != nullptr) {
- return remainder;
- }
- }
-
- // Nothing worked with this wildcard.
- return nullptr;
- } else {
- // Standard compare of 2 chars. Note that *pszSring might be 0
- // here, but then we never get a match on *pszMask that has always
- // a value while inside this loop.
- if (CharUpperA(MAKEINTRESOURCEA(MAKELONG(*pszString++, 0))) !=
- CharUpperA(MAKEINTRESOURCEA(MAKELONG(*pszMatch++, 0))))
- return nullptr;
- }
- }
-
- // successful match if the input string was completely consumed
- if (*pszString == '\0') {
- while ((*pszMatch == '*') || (*pszMatch == '<')) {
- ++pszMatch;
- }
- return pszMatch;
- } else {
- return nullptr;
- }
-}
-
-namespace usvfs::shared::wildcard
-{
-
-bool Match(LPCWSTR pszString, LPCWSTR pszMatch)
-{
- if (*pszString == L'.') {
- // cmd.exe seems to ignore
- return Match(pszString + 1, pszMatch);
- } else {
- size_t len = wcslen(pszMatch);
- if ((len > 2) && (wcscmp(pszMatch + len - 2, L".*") == 0)) {
- // cmd.exe seems to completely ignore .* at the end.
- std::wstring temp(pszMatch, pszMatch + len - 2);
- return IsInnerMatch(pszString, temp.c_str());
- }
- return IsInnerMatch(pszString, pszMatch);
- }
-}
-
-bool Match(LPCSTR pszString, LPCSTR pszMatch)
-{
- if (*pszString == '.') {
- // cmd.exe seems to ignore
- return Match(pszString + 1, pszMatch);
- } else {
- size_t len = strlen(pszMatch);
- LPCSTR res = nullptr;
- if ((len > 2) && (strcmp(pszMatch + len - 2, ".*") == 0)) {
- // cmd.exe seems to completely ignore .* at the end.
- std::string temp(pszMatch, pszMatch + len - 2);
- res = InnerMatch(pszString, temp.c_str());
- } else {
- res = InnerMatch(pszString, pszMatch);
- }
- return ((res != nullptr) && (*res == '\0'));
- }
-}
-
-LPCSTR PartialMatch(LPCSTR pszString, LPCSTR pszMatch)
-{
- if (*pszString == '.') {
- // cmd.exe seems to ignore dots at the start
- return PartialMatch(pszString + 1, pszMatch);
- } else {
- size_t len = strlen(pszMatch);
- if ((len > 2) && (strcmp(pszMatch + len - 2, ".*") == 0)) {
- // in cmd.exe there seems to be no difference between <something>* and
- // <something>*.*
- std::string temp(pszMatch, pszMatch + len - 2);
- LPCSTR pos = InnerMatch(pszString, temp.c_str());
- if (pos != nullptr) {
- if (*pos == '\0') {
- return pszMatch + strlen(pszMatch);
- } else {
- return pszMatch + (pos - temp.c_str());
- }
- }
- }
- return InnerMatch(pszString, pszMatch);
- }
-}
-
-} // namespace usvfs::shared::wildcard
diff --git a/libs/usvfs/src/shared/wildcard.h b/libs/usvfs/src/shared/wildcard.h
deleted file mode 100644
index bf5b4a3..0000000
--- a/libs/usvfs/src/shared/wildcard.h
+++ /dev/null
@@ -1,67 +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/>.
-*/
-/// Wildcard matching code
-/// by Martin Richter
-/// licensed under The Code Project Open License (CPOL)
-
-#pragma once
-
-#include "windows_sane.h"
-
-namespace usvfs::shared::wildcard
-{
-
-/**
- * @brief match string to wildcard windows-style
- * @param pszString Input string to match
- * @param pszMatch Match mask that may contain wildcards like ? and *
- * @note A ? sign matches any character, except an empty string.
- * @note A * sign matches any string inclusive an empty string.
- * @note Characters are compared caseless.
- * @return true if the string matches the pattern
- */
-bool Match(LPCWSTR pszString, LPCWSTR pszMatch);
-
-/**
- * @brief match string to wildcard windows-style
- * @param pszString Input string to match
- * @param pszMatch Match mask that may contain wildcards like ? and *
- * @note A ? sign matches any character, except an empty string.
- * @note A * sign matches any string inclusive an empty string.
- * @note Characters are compared caseless.
- * @return true if the string matches the pattern
- */
-bool Match(LPCSTR pszString, LPCSTR pszMatch);
-
-/**
- * @brief match string to wildcard windows-style
- * @param pszString Input string to match
- * @param pszMatch Match mask that may contain wildcards like ? and *
- * @note A ? sign matches any character, except an empty string.
- * @note A * sign matches any string inclusive an empty string.
- * @note Characters are compared caseless.
- * @return the "not-consumed" remainder of the pattern. If this points to a
- * zero terminator, this was a full match.
- * Returns nullptr if no match is possible
- */
-LPCSTR PartialMatch(LPCSTR pszString, LPCSTR pszMatch);
-
-} // namespace usvfs::shared::wildcard
diff --git a/libs/usvfs/src/shared/winapi.cpp b/libs/usvfs/src/shared/winapi.cpp
deleted file mode 100644
index e28a13c..0000000
--- a/libs/usvfs/src/shared/winapi.cpp
+++ /dev/null
@@ -1,490 +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 "winapi.h"
-#include "exceptionex.h"
-#include "logging.h"
-#include "ntdll_declarations.h"
-#include "stringcast.h"
-#include "stringutils.h"
-#include "unicodestring.h"
-
-namespace winapi::ansi
-{
-
-std::string getModuleFileName(HMODULE module, HANDLE process)
-{
- std::wstring result = wide::getModuleFileName(module, process);
- return usvfs::shared::string_cast<std::string>(result);
-}
-
-std::string getCurrentDirectory()
-{
- std::string result;
- DWORD required = GetCurrentDirectoryA(0, nullptr);
- if (required == 0UL) {
- throw usvfs::shared::windows_error("failed to determine current working directory");
- }
- result.resize(required);
- GetCurrentDirectoryA(required, &result[0]);
- result.resize(required - 1);
- return result;
-}
-
-std::pair<std::string, std::string> getFullPathName(LPCSTR fileName)
-{
- static const int INIT_SIZE = 128;
- std::string result;
- result.resize(INIT_SIZE);
- LPSTR filePart = nullptr;
- DWORD requiredSize = GetFullPathNameA(fileName, INIT_SIZE, &result[0], &filePart);
- if (requiredSize >= INIT_SIZE) {
- result.resize(requiredSize);
- GetFullPathNameA(fileName, requiredSize, &result[0], &filePart);
- }
- if (requiredSize != 0UL) {
- return std::make_pair(result, std::string(filePart != nullptr ? filePart : ""));
- } else {
- return make_pair(result, std::string());
- }
-}
-
-} // namespace winapi::ansi
-
-namespace winapi::wide
-{
-
-std::wstring getModuleFileName(HMODULE module, HANDLE process)
-{
- std::wstring result;
- result.resize(64);
- DWORD rc = 0UL;
-
- while ((rc = (process == INVALID_HANDLE_VALUE)
- ? ::GetModuleFileNameW(module, &result[0],
- static_cast<DWORD>(result.size()))
- : ::GetModuleFileNameExW(process, module, &result[0],
- static_cast<DWORD>(result.size()))) ==
- result.size()) {
- result.resize(result.size() * 2);
- }
-
- if (rc == 0UL) {
- if (::GetLastError() == ERROR_PARTIAL_COPY) {
-#if BOOST_ARCH_X86_64
- return L"unknown (32-bit process)";
-#else
- return L"unknown (64-bit process)";
-#endif
- } else {
- throw usvfs::shared::windows_error("failed to retrieve module file name");
- }
- }
-
- result.resize(rc);
-
- return result;
-}
-
-std::pair<std::wstring, std::wstring> getFullPathName(LPCWSTR fileName)
-{
- wchar_t buf1[MAX_PATH];
- std::vector<wchar_t> buf2;
- wchar_t* result = buf1;
- LPWSTR filePart = nullptr;
- DWORD requiredSize = GetFullPathNameW(fileName, MAX_PATH, result, &filePart);
- if (requiredSize >= MAX_PATH) {
- buf2.resize(requiredSize);
- result = &buf2[0];
- requiredSize = GetFullPathNameW(fileName, requiredSize, result, &filePart);
- }
- return make_pair(std::wstring(result, requiredSize),
- std::wstring((requiredSize && filePart) ? filePart : L""));
-}
-
-std::wstring getCurrentDirectory()
-{
- // really great api this (::GetCurrentDirectoryW)
- // - if it succeeds, returns size in characters WITHOUT zero termination
- // - if it fails due to buffer too small, returns size in characters WITH zero
- // termination
- // - if it fails for other reasons, returns 0
- std::wstring result;
- DWORD required = GetCurrentDirectoryW(0, nullptr);
- if (required == 0UL) {
- throw usvfs::shared::windows_error("failed to determine current working directory");
- }
- result.resize(required);
- GetCurrentDirectoryW(required, &result[0]);
- result.resize(required - 1);
- return result;
-}
-
-std::wstring getKnownFolderPath(REFKNOWNFOLDERID folderID)
-{
- PWSTR writablePath;
-
- ::SHGetKnownFolderPath(folderID, 0, nullptr, &writablePath);
-
- ON_BLOCK_EXIT([writablePath]() {
- ::CoTaskMemFree(writablePath);
- });
-
- return std::wstring(writablePath);
-}
-
-} // namespace winapi::wide
-
-namespace winapi::ex
-{
-
-std::pair<uintptr_t, uintptr_t> getSectionRange(HANDLE moduleHandle)
-{
- std::pair<uintptr_t, uintptr_t> result;
- bool found = false;
- uintptr_t exeModule = reinterpret_cast<uintptr_t>(moduleHandle);
- if (exeModule == 0) {
- throw std::runtime_error("failed to determine address range of executable");
- }
-
- std::pair<uintptr_t, uintptr_t> totalRange{UINT_MAX, 0};
-
- PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(exeModule);
- PIMAGE_NT_HEADERS ntHeader =
- reinterpret_cast<PIMAGE_NT_HEADERS>(exeModule + dosHeader->e_lfanew);
- PIMAGE_SECTION_HEADER sectionHeader =
- reinterpret_cast<PIMAGE_SECTION_HEADER>(ntHeader + 1);
- for (int i = 0; i < ntHeader->FileHeader.NumberOfSections && !found; ++i) {
- if (memcmp(sectionHeader->Name, ".text", 5) == 0) {
- result.first = exeModule + sectionHeader->VirtualAddress;
- result.second = result.first + sectionHeader->Misc.VirtualSize;
- found = true;
- } else {
- uintptr_t start = exeModule + sectionHeader->VirtualAddress;
- totalRange.first = std::min(totalRange.first, start);
- totalRange.second = std::max<uintptr_t>(totalRange.second,
- start + sectionHeader->Misc.VirtualSize);
- }
- ++sectionHeader;
- }
-
- if (!found) {
- return totalRange;
- }
-
- return result;
-}
-
-OSVersion getOSVersion()
-{
- RTL_OSVERSIONINFOEXW versionInfo;
- ZeroMemory(&versionInfo, sizeof(RTL_OSVERSIONINFOEXW));
- versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
- RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo);
-
- OSVersion result;
- result.major = versionInfo.dwMajorVersion;
- result.minor = versionInfo.dwMinorVersion;
- result.build = versionInfo.dwBuildNumber;
- result.platformid = versionInfo.dwPlatformId;
- result.servicpack =
- versionInfo.wServicePackMajor << 16 | versionInfo.wServicePackMinor;
- return result;
-}
-
-} // namespace winapi::ex
-
-namespace winapi::ex::ansi
-{
-
-std::string errorString(DWORD errorCode)
-{
- std::ostringstream finalMessage;
-
- LPSTR buffer = nullptr;
-
- DWORD currentErrorCode = GetLastError();
-
- errorCode =
- errorCode != std::numeric_limits<DWORD>::max() ? errorCode : currentErrorCode;
-
- // TODO: the message is not english?
- if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode,
- 0 //, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
- ,
- (LPSTR)&buffer, 0, nullptr) == 0) {
- finalMessage << "(unknown error [" << errorCode << "])";
- } else {
- if (buffer != nullptr) {
- size_t end = strlen(buffer) - 1;
- while ((buffer[end] == '\n') || (buffer[end] == '\r')) {
- buffer[end--] = '\0';
- }
- finalMessage << "(" << buffer << " [" << errorCode << "])";
- LocalFree(buffer); // allocated by FormatMessage
- }
- }
-
- SetLastError(currentErrorCode); // restore error code because FormatMessage might
- // have modified it
- return finalMessage.str();
-}
-
-std::string toString(const FILETIME& time)
-{
- SYSTEMTIME temp;
- FileTimeToSystemTime(&time, &temp);
- std::ostringstream stream;
- stream << temp.wYear << "-" << temp.wMonth << "-" << temp.wDay << temp.wHour << ":"
- << temp.wMinute << ":" << temp.wSecond;
- return stream.str();
-}
-
-LPCSTR GetBaseName(LPCSTR string)
-{
- LPCSTR result = string + strlen(string) - 1;
- while (result > string) {
- if ((*result == '\\') || (*result == '/')) {
- ++result;
- break;
- } else {
- --result;
- }
- }
- return result;
-}
-
-} // namespace winapi::ex::ansi
-
-namespace winapi::ex::wide
-{
-
-bool fileExists(LPCWSTR fileName, bool* isDirectory)
-{
- DWORD attrib = GetFileAttributesW(fileName);
-
- if (attrib == INVALID_FILE_ATTRIBUTES) {
- return false;
- } else {
- if (isDirectory != nullptr) {
- *isDirectory = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
- return true;
- }
-}
-
-std::wstring errorString(DWORD errorCode)
-{
- std::wostringstream finalMessage;
-
- LPWSTR buffer = nullptr;
-
- DWORD currentErrorCode = GetLastError();
-
- errorCode =
- errorCode != std::numeric_limits<DWORD>::max() ? errorCode : currentErrorCode;
-
- // TODO: the message is not english?
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode,
- 0 //, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
- ,
- (LPWSTR)&buffer, 0, nullptr) == 0) {
- finalMessage << L"(unknown error [" << errorCode << "])";
- } else {
- if (buffer != nullptr) {
- size_t end = wcslen(buffer) - 1;
- while ((buffer[end] == L'\n') || (buffer[end] == L'\r')) {
- buffer[end--] = L'\0';
- }
- finalMessage << L"(" << buffer << L" [" << errorCode << L"])";
- LocalFree(buffer); // allocated by FormatMessage
- }
- }
-
- SetLastError(currentErrorCode); // restore error code because FormatMessage might
- // have modified it
- return finalMessage.str();
-}
-
-std::wstring toString(const FILETIME& time)
-{
- SYSTEMTIME temp;
- FileTimeToSystemTime(&time, &temp);
- std::wostringstream stream;
- stream << temp.wYear << "-" << temp.wMonth << "-" << temp.wDay << temp.wHour << ":"
- << temp.wMinute << ":" << temp.wSecond;
- return stream.str();
-}
-
-LPCWSTR GetBaseName(LPCWSTR string)
-{
- LPCWSTR result;
- if ((string == nullptr) || (string[0] == L'\0')) {
- result = string;
- } else {
- result = string + wcslen(string) - 1;
- }
-
- while (result > string) {
- if ((*result == L'\\') || (*result == L'/')) {
- ++result;
- break;
- } else {
- --result;
- }
- }
- return result;
-}
-
-LPWSTR GetBaseName(LPWSTR path)
-{
- LPCWSTR result = GetBaseName(static_cast<LPCWSTR>(path));
- return const_cast<LPWSTR>(result);
-}
-
-std::wstring getSectionName(PVOID addressIn, HANDLE process)
-{
- if (process == nullptr) {
- process = GetCurrentProcess();
- }
- HMODULE modules[1024];
- intptr_t address = reinterpret_cast<intptr_t>(addressIn);
- DWORD required;
- if (::EnumProcessModules(process, modules, sizeof(modules), &required)) {
- for (DWORD i = 0; i < (std::min<DWORD>(1024UL, required) / sizeof(HMODULE)); ++i) {
- std::pair<intptr_t, intptr_t> range = getSectionRange(modules[i]);
- if ((address > range.first) && (address < range.second)) {
- try {
- return winapi::wide::getModuleFileName(modules[i], process);
- } catch (const std::exception&) {
- return std::wstring(L"unknown");
- }
- }
- }
- }
- return std::wstring(L"unknown");
-}
-
-std::vector<FileResult> quickFindFiles(LPCWSTR directoryName, LPCWSTR pattern)
-{
- std::vector<FileResult> result;
-
- static const unsigned int BUFFER_SIZE = 1024;
-
- HANDLE hdl = CreateFileW(directoryName, GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
-
- ON_BLOCK_EXIT([hdl]() {
- CloseHandle(hdl);
- });
-
- uint8_t buffer[BUFFER_SIZE];
-
- NTSTATUS res = STATUS_SUCCESS; // status success
- while (res == STATUS_SUCCESS) {
- IO_STATUS_BLOCK status;
-
- res = NtQueryDirectoryFile(
- hdl, nullptr, nullptr, nullptr, &status, buffer, BUFFER_SIZE,
- FileFullDirectoryInformation, FALSE,
- static_cast<PUNICODE_STRING>(usvfs::UnicodeString(pattern)), FALSE);
- if (res == STATUS_SUCCESS) {
- FILE_FULL_DIR_INFORMATION* info =
- reinterpret_cast<FILE_FULL_DIR_INFORMATION*>(buffer);
- void* endPos = buffer + status.Information;
- while (info < endPos) {
- FileResult file;
- file.fileName =
- std::wstring(info->FileName, info->FileNameLength / sizeof(wchar_t));
- file.attributes = info->FileAttributes;
-
- result.push_back(file);
- if (info->NextEntryOffset == 0) {
- break;
- } else {
- info = reinterpret_cast<FILE_FULL_DIR_INFORMATION*>(
- reinterpret_cast<uint8_t*>(info) + info->NextEntryOffset);
- }
- }
- }
- }
-
- return result;
-}
-
-bool createPath(boost::filesystem::path path, LPSECURITY_ATTRIBUTES securityAttributes)
-{
- // sanity and guaranteed recursion end:
- if (!path.has_relative_path())
- throw usvfs::shared::windows_error(
- "createPath() 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 usvfs::shared::windows_error("createPath() called on a file: " +
- path.string());
- }
- if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND)
- throw usvfs::shared::windows_error(
- "createPath() GetFileAttributesW failed on: " + path.string(), err);
-
- if (err != ERROR_FILE_NOT_FOUND) // ERROR_FILE_NOT_FOUND means parent directory
- // already exists
- createPath(path.parent_path(),
- securityAttributes); // otherwise create parent directory (recursively)
-
- BOOL res = CreateDirectoryW(path.c_str(), securityAttributes);
- if (!res) {
- err = GetLastError();
- throw usvfs::shared::windows_error(
- "createPath() CreateDirectoryW failed on: " + path.string(), err);
- }
- return true;
-}
-
-std::wstring getWindowsBuildLab(bool ex)
-{
- HKEY hKey = nullptr;
- auto res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
- LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0,
- KEY_READ, &hKey);
- if (res != ERROR_SUCCESS || !hKey)
- return L"Opening HKLM Windows NT\\CurrentVersion failed?!";
- WCHAR buf[200];
- DWORD size = static_cast<DWORD>(sizeof(buf));
- res = RegQueryValueExW(hKey, ex ? L"BuildLabEx" : L"BuildLab", NULL, NULL,
- reinterpret_cast<LPBYTE>(buf), &size);
- if (res != ERROR_SUCCESS || size > sizeof(buf))
- return ex ? L"BuildLabEx reg value not found?!" : L"BuildLab reg value not found?!";
- size /= sizeof(buf[0]);
- if (size && !buf[size - 1])
- --size;
- return std::wstring(buf, size);
-}
-
-} // namespace winapi::ex::wide
diff --git a/libs/usvfs/src/shared/winapi.h b/libs/usvfs/src/shared/winapi.h
deleted file mode 100644
index f94eff6..0000000
--- a/libs/usvfs/src/shared/winapi.h
+++ /dev/null
@@ -1,610 +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 "logging.h"
-#include "stringcast.h"
-#include "windows_sane.h"
-#include <ShlObj.h>
-
-#define ALIAS(alias, original) \
- template <typename... Args> \
- auto alias(Args&&... args) -> decltype(original(std::forward<Args>(args)...)) \
- { \
- return original(std::forward<Args>(args)...); \
- }
-
-#define ALIAST(alias, original) \
- template <typename T, typename... Args> \
- auto alias<T>(Args && ... args) \
- -> decltype(original<T>(std::forward<Args>(args)...)) \
- { \
- return original<T>(std::forward<Args>(args)...); \
- }
-
-namespace winapi
-{
-
-struct parameter_error : public std::runtime_error
-{
- parameter_error(const std::string& msg) : runtime_error(msg) {}
-};
-
-} // namespace winapi
-
-namespace winapi::process
-{
-
-/**
- * @brief result of process creation
- */
-struct Result
-{
- Result()
- {
- ::ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
- ::ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
- startupInfo.cb = sizeof(STARTUPINFO);
- }
-
- Result(Result&& reference)
- : valid(reference.valid), startupInfo(reference.startupInfo),
- processInfo(reference.processInfo), errorCode(reference.errorCode)
- {
- reference.valid = false;
- }
-
- ~Result()
- {
- if (valid) {
- CloseHandle(processInfo.hProcess);
- CloseHandle(processInfo.hThread);
- }
-
- if (stdoutPipe != INVALID_HANDLE_VALUE) {
- CloseHandle(stdoutPipe);
- }
- }
-
- Result(const Result&) = delete;
-
- size_t readStdout(std::vector<uint8_t>& buffer, bool& eof)
- {
- if (stdoutPipe != INVALID_HANDLE_VALUE) {
- DWORD read;
- BOOL res = ReadFile(stdoutPipe, &buffer[0], static_cast<DWORD>(buffer.size()),
- &read, nullptr);
- eof = (res == TRUE) && (read == 0);
- return static_cast<size_t>(read);
- } else {
- eof = true;
- return 0;
- }
- }
-
- bool valid{false};
- STARTUPINFO startupInfo;
- PROCESS_INFORMATION processInfo;
- DWORD errorCode{0UL};
-
- HANDLE stdoutPipe{INVALID_HANDLE_VALUE};
-};
-
-/**
- * @brief internal class to handle process creation with named parameters.
- */
-template <typename CharT>
-class _Create
-{
-public:
- _Create(const std::basic_string<CharT>& binaryName);
- _Create(const _Create<CharT>& reference) = delete;
- _Create<CharT>& operator=(const _Create<CharT>& reference) = delete;
-
- _Create(_Create<CharT>&& reference)
- : m_CurrentDirectory(std::move(reference.m_CurrentDirectory)),
- m_ProcessAttributes(reference.m_ProcessAttributes),
- m_ThreadAttributes(reference.m_ThreadAttributes),
- m_InheritHandles(reference.m_InheritHandles),
- m_CreationFlags(std::move(reference.m_CreationFlags)),
- m_Executed(reference.m_Executed)
- {
- // stringstream should be moveable but it seems it isn't on mingw
- m_CommandLine << reference.m_CommandLine.rdbuf();
- }
-
- /// named parameter "argument". May be called repeatedly. This is
- /// directly appended to the command line with a separating space. No
- /// quoting happens
- template <typename ArgT>
- _Create& argument(const ArgT& argin)
- {
- m_CommandLine << " " << argin;
- return *this;
- }
-
- template <typename ArgT>
- _Create& arg(const ArgT& argin)
- {
- return this->argument(argin);
- }
-
- template <typename IterT>
- _Create& arguments(IterT begin, IterT end)
- {
- for (; begin != end; ++begin) {
- m_CommandLine << " " << *begin;
- }
-
- return *this;
- }
-
- /// @brief set the working directory for the process
- _Create& workingDirectory(const std::basic_string<CharT>& path);
-
- /// @brief set process attributes
- _Create& processAttributes(SECURITY_ATTRIBUTES* attributes);
-
- /// @brief set thread attributes
- _Create& threadAttributes(SECURITY_ATTRIBUTES* attributes);
-
- /// @brief activate inheriting handles
- _Create& inheritHandles();
-
- /// @brief have the process start suspended
- _Create& suspended();
-
- /// @brief set the process up to output stout to a pipe which can be
- /// retrieved through the result object
- _Create& stdoutPipe();
-
- /// @brief end the named parameter cascade and create the process
- Result operator()()
- {
- m_CommandLine.seekp(0, std::ios::end);
- unsigned int length = static_cast<unsigned int>(m_CommandLine.tellp());
- std::unique_ptr<CharT[]> clBuffer(new CharT[length + 1]);
- memset(clBuffer.get(), 0, (length + 1) * sizeof(CharT));
- memcpy(clBuffer.get(), m_CommandLine.str().c_str(), length * sizeof(CharT));
- Result result;
-
- if (m_StdoutPipe) {
- result.stdoutPipe = setupPipe(result.startupInfo.hStdOutput);
- result.startupInfo.dwFlags |= STARTF_USESTDHANDLES;
- }
-
- result.valid =
- createProcessInt(nullptr, clBuffer.get(), m_ProcessAttributes,
- m_ThreadAttributes, m_InheritHandles, m_CreationFlags, nullptr,
- m_CurrentDirectory.length() > 0 ? m_CurrentDirectory.c_str()
- : nullptr,
- &result.startupInfo, &result.processInfo) == TRUE;
-
- if (m_Stdout != INVALID_HANDLE_VALUE) {
- // got to close the write end of pipes
- CloseHandle(result.startupInfo.hStdOutput);
- }
-
- if (result.valid) {
- result.errorCode = NOERROR;
- } else {
- result.errorCode = GetLastError();
- }
- return result;
- }
-
-private:
- static BOOL createProcessInt(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
- SECURITY_ATTRIBUTES* lpProcessAttributes,
- SECURITY_ATTRIBUTES* lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags,
- LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
- LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation)
- {
- return ::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
- lpThreadAttributes, bInheritHandles, dwCreationFlags,
- lpEnvironment, lpCurrentDirectory, lpStartupInfo,
- lpProcessInformation);
- }
-
- static BOOL createProcessInt(LPCSTR lpApplicationName, LPSTR lpCommandLine,
- SECURITY_ATTRIBUTES* lpProcessAttributes,
- SECURITY_ATTRIBUTES* lpThreadAttributes,
- BOOL bInheritHandles, DWORD dwCreationFlags,
- LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
- LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation)
- {
- std::wstring executable;
- if (lpApplicationName != nullptr) {
- executable = usvfs::shared::string_cast<std::wstring>(lpApplicationName);
- }
-
- std::wstring cmdline;
- if (lpCommandLine != nullptr) {
- cmdline = usvfs::shared::string_cast<std::wstring>(lpCommandLine);
- }
-
- std::wstring cwd;
- if (lpCurrentDirectory != nullptr) {
- cwd = usvfs::shared::string_cast<std::wstring>(lpCurrentDirectory);
- }
-
- return ::CreateProcessW(lpApplicationName != nullptr ? executable.c_str() : nullptr,
- lpCommandLine != nullptr ? &cmdline[0] : nullptr,
- lpProcessAttributes, lpThreadAttributes, bInheritHandles,
- dwCreationFlags, lpEnvironment,
- lpCurrentDirectory != nullptr ? cwd.c_str() : nullptr,
- lpStartupInfo, lpProcessInformation);
- }
-
- HANDLE setupPipe(HANDLE& childHandle)
- {
- SECURITY_ATTRIBUTES attr;
- attr.nLength = sizeof(SECURITY_ATTRIBUTES);
- attr.bInheritHandle = TRUE;
- attr.lpSecurityDescriptor = nullptr;
-
- HANDLE pipe[2];
-
- CreatePipe(&pipe[0], &pipe[1], &attr, 0);
- SetHandleInformation(pipe[0], HANDLE_FLAG_INHERIT, 0);
-
- childHandle = pipe[1];
-
- return pipe[0];
- }
-
-private:
- std::basic_stringstream<CharT> m_CommandLine;
- std::basic_string<CharT> m_CurrentDirectory{};
- SECURITY_ATTRIBUTES* m_ProcessAttributes{nullptr};
- SECURITY_ATTRIBUTES* m_ThreadAttributes{nullptr};
- BOOL m_InheritHandles{false};
- DWORD m_CreationFlags{0UL};
- bool m_Executed{false};
- bool m_StdoutPipe{false};
-
- HANDLE m_Stdout{INVALID_HANDLE_VALUE};
-};
-
-} // namespace winapi::process
-
-namespace winapi::file
-{
-/**
- * @brief internal class to handle file creation (opening) with named
- * parameters.
- */
-template <typename CharT, DWORD DefaultDisposition>
-class _Create
-{
-public:
- _Create(const std::basic_string<CharT>& fileName) : m_FileName(fileName) {}
-
- _Create& access(DWORD desiredAccess)
- {
- m_DesiredAccess = desiredAccess;
- return *this;
- }
-
- _Create& share(DWORD shareMode)
- {
- m_ShareMode = shareMode;
- return *this;
- }
-
- _Create& createAlways()
- {
- m_CreationDisposition = CREATE_ALWAYS;
- return *this;
- }
-
- _Create& openAlways()
- {
- m_CreationDisposition = OPEN_ALWAYS;
- return *this;
- }
-
- _Create& security(SECURITY_ATTRIBUTES* attributes)
- {
- m_SecurityAttributes = attributes;
- return *this;
- }
-
- _Create& templateFile(HANDLE templateFile)
- {
- m_Template = templateFile;
- return *this;
- }
-
- /// @brief end the named parameter cascade and open the file
- HANDLE operator()()
- {
- return callDelegate(
- std::integral_constant<bool, sizeof(CharT) == sizeof(wchar_t)>());
- }
-
-private:
- HANDLE callDelegate(std::true_type)
- {
- return ::CreateFileW(m_FileName.c_str(), m_DesiredAccess, m_ShareMode,
- m_SecurityAttributes, m_CreationDisposition, m_Flags,
- m_Template);
- }
-
- HANDLE callDelegate(std::false_type)
- {
- return ::CreateFileA(m_FileName.c_str(), m_DesiredAccess, m_ShareMode,
- m_SecurityAttributes, m_CreationDisposition, m_Flags,
- m_Template);
- }
-
-private:
- std::basic_string<CharT> m_FileName;
- DWORD m_DesiredAccess{GENERIC_ALL};
- DWORD m_ShareMode{0UL};
- DWORD m_CreationDisposition{DefaultDisposition};
- DWORD m_Flags{FILE_ATTRIBUTE_NORMAL};
- HANDLE m_Template{nullptr};
- SECURITY_ATTRIBUTES* m_SecurityAttributes{nullptr};
-};
-
-} // namespace winapi::file
-
-namespace winapi::ansi
-{
-
-std::string getModuleFileName(HMODULE module, HANDLE process = INVALID_HANDLE_VALUE);
-std::pair<std::string, std::string> getFullPathName(LPCSTR fileName);
-std::string getCurrentDirectory();
-typedef process::_Create<char> createProcess;
-typedef file::_Create<char, CREATE_NEW> createFile;
-typedef file::_Create<char, OPEN_EXISTING> openFile;
-
-} // namespace winapi::ansi
-
-namespace winapi::wide
-{
-
-std::wstring getModuleFileName(HMODULE module, HANDLE process = INVALID_HANDLE_VALUE);
-std::pair<std::wstring, std::wstring> getFullPathName(LPCWSTR fileName);
-std::wstring getCurrentDirectory();
-std::wstring getKnownFolderPath(REFKNOWNFOLDERID folderID);
-
-typedef process::_Create<wchar_t> createProcess;
-typedef file::_Create<wchar_t, CREATE_NEW> createFile;
-typedef file::_Create<wchar_t, OPEN_EXISTING> openFile;
-
-} // namespace winapi::wide
-
-/**
- * useful convenience functions close to the api
- */
-namespace winapi::ex
-{
-
-/**
- * @brief retrieve the address range covering the code section of a module
- * @param moduleHandle handle to the module
- * @return start and end address of the code section
- * @note the code section can only be identified if it has the standardized section name
- * ".text" Otherwise the whole address range of all sections in the module is returned.
- * This happens for compressed exectuables for example
- */
-std::pair<uintptr_t, uintptr_t> getSectionRange(HANDLE moduleHandle);
-
-struct OSVersion
-{
- DWORD major;
- DWORD minor;
- DWORD build;
- DWORD platformid;
- DWORD servicpack;
-};
-
-OSVersion getOSVersion();
-
-} // namespace winapi::ex
-
-namespace winapi::ex::ansi
-{
-
-/**
- * @brief retrieve an error string for a windows error message
- * @param errorCode the error code to look up. If this is left at the default,
- * ::GetLastError is used
- * @return string representation of the error. Currently this is localized
- */
-std::string errorString(DWORD errorCode = std::numeric_limits<DWORD>::max());
-
-/**
- * @brief convert filetime to string
- * @param time time to convert
- * @return a string representation (currently only supports utc and iso format with
- * second precision)
- */
-std::string toString(const FILETIME& time);
-
-/**
- * @brief find file name in a windows file path
- * @param path the path to search in
- * @return the file name of the path or an empty string if the path ends on
- * a slash
- * @note this function doesn't access the file system so it doesn't depend
- * on whether the file actually exists. This also means it can't
- * determine if a path that doesn't end on a slash refers to a file or
- * directory
- * @note the return value is a pointer into the same buffer, no copy is
- * created
- */
-LPCSTR GetBaseName(LPCSTR string);
-
-} // namespace winapi::ex::ansi
-
-namespace winapi::ex::wide
-{
-/**
- * retrieve the name of the binary section containing the specified address
- * @param address the address to test
- * @param process the process for which to retrieve the section. If this is
- * nullptr, the current process is analized.
- * @return name of the section or "unknown" if no matching section was found
- */
-std::wstring getSectionName(PVOID address, HANDLE process = nullptr);
-
-/**
- * @brief test if a file exists
- * @param path path to check
- * @param isDirectory (optional) if this isn't null, it will be set to true if the path
- * specifies a directory, false otherwise
- * @return true if the file (or directory) exists.
- */
-bool fileExists(LPCWSTR fileName, bool* isDirectory = nullptr);
-
-/**
- * @brief retrieve an error string for a windows error message
- * @param errorCode the error code to look up. If this is left at the default,
- * ::GetLastError is used
- * @return string representation of the error. Currently this is localized
- */
-std::wstring errorString(DWORD errorCode = std::numeric_limits<DWORD>::max());
-
-/**
- * @brief convert filetime to string
- * @param time time to convert
- * @return a string representation (currently only supports utc and iso format with
- * second precision)
- */
-std::wstring toString(const FILETIME& time);
-
-/**
- * @brief find file name in a windows file path
- * @param path the path to search in
- * @return the file name of the path or an empty string if the path ends on
- * a slash
- * @note this function doesn't access the file system so it doesn't depend
- * on whether the file actually exists. This also means it can't
- * determine if a path that doesn't end on a slash refers to a file or
- * directory
- * @note the return value is a pointer into the same buffer, no copy is
- * created
- */
-LPCWSTR GetBaseName(LPCWSTR path);
-
-/**
- * @see const-variant of this function
- */
-LPWSTR GetBaseName(LPWSTR path);
-
-struct FileResult
-{
- std::wstring fileName;
- ULONG attributes;
-};
-
-/**
- * @brief a quick function to find all files in a directory or files following a
- * pattern. This uses NtQueryDirectoryFile api internally so it should be faster than
- * the usual FindFirstFile/FindNextFile pattern
- * @param directoryName name of the directory to search in
- * @param pattern name pattern that needs to match
- * @return the list of files found
- */
-std::vector<FileResult> quickFindFiles(LPCWSTR directoryName, LPCWSTR pattern);
-
-/**
- * @brief create the specified directory including all intermediate
- * directories
- * @param path the path to create
- * @param securityAttributes the security attributes to use for all created
- * directories. if this is null (default), the standard attributes
- * are used
- * @return true if the directory (and possibly parent directories) were actually created
- * and false if the directory already existed. Throws exceptions on failure.
- */
-bool createPath(boost::filesystem::path path,
- LPSECURITY_ATTRIBUTES securityAttributes = nullptr);
-inline bool createPath(LPCWSTR path, LPSECURITY_ATTRIBUTES securityAttributes = nullptr)
-{
- return createPath(boost::filesystem::path(path), securityAttributes);
-}
-
-std::wstring getWindowsBuildLab(bool ex = false);
-
-} // namespace winapi::ex::wide
-
-namespace winapi::process
-{
-
-template <typename CharT>
-_Create<CharT>::_Create(const std::basic_string<CharT>& binaryName)
-{
- if (binaryName.length() > MAX_PATH) {
- throw parameter_error("executable filename can't be longer than 260 characters");
- }
- m_CommandLine << "\"" << binaryName << "\"";
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::workingDirectory(const std::basic_string<CharT>& path)
-{
- m_CurrentDirectory = path;
- return *this;
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::processAttributes(SECURITY_ATTRIBUTES* attributes)
-{
- m_ProcessAttributes = attributes;
- return *this;
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::threadAttributes(SECURITY_ATTRIBUTES* attributes)
-{
- m_ThreadAttributes = attributes;
- return *this;
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::inheritHandles()
-{
- m_InheritHandles = true;
- return *this;
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::suspended()
-{
- m_CreationFlags |= CREATE_SUSPENDED;
- return *this;
-}
-
-template <typename CharT>
-_Create<CharT>& _Create<CharT>::stdoutPipe()
-{
- m_StdoutPipe = true;
- return *this;
-}
-
-} // namespace winapi::process
diff --git a/libs/usvfs/src/shared/windows_sane.h b/libs/usvfs/src/shared/windows_sane.h
deleted file mode 100644
index 7bb3726..0000000
--- a/libs/usvfs/src/shared/windows_sane.h
+++ /dev/null
@@ -1,28 +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
-
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
diff --git a/libs/usvfs/src/thooklib/CMakeLists.txt b/libs/usvfs/src/thooklib/CMakeLists.txt
deleted file mode 100644
index 48426db..0000000
--- a/libs/usvfs/src/thooklib/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(asmjit CONFIG REQUIRED)
-find_library(LIBUDIS86_LIBRARY libudis86)
-find_package(spdlog CONFIG REQUIRED)
-
-file(GLOB sources CONFIGURE_DEPENDS "*.cpp" "*.h")
-source_group("" FILES ${sources})
-
-add_library(thooklib STATIC ${sources})
-target_link_libraries(thooklib PRIVATE shared asmjit::asmjit ${LIBUDIS86_LIBRARY} spdlog::spdlog_header_only)
-target_include_directories(thooklib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-set_target_properties(thooklib PROPERTIES FOLDER injection)
-target_precompile_headers(shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../shared/pch.h)
diff --git a/libs/usvfs/src/thooklib/asmjit_sane.h b/libs/usvfs/src/thooklib/asmjit_sane.h
deleted file mode 100644
index 365622a..0000000
--- a/libs/usvfs/src/thooklib/asmjit_sane.h
+++ /dev/null
@@ -1,29 +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
-
-#pragma warning(push)
-#pragma warning(disable : 4201)
-#pragma warning(disable : 4244)
-#pragma warning(disable : 4245)
-#include <asmjit/asmjit.h>
-#include <asmjit/x86.h>
-#pragma warning(pop)
diff --git a/libs/usvfs/src/thooklib/hooklib.cpp b/libs/usvfs/src/thooklib/hooklib.cpp
deleted file mode 100644
index 9dbbafa..0000000
--- a/libs/usvfs/src/thooklib/hooklib.cpp
+++ /dev/null
@@ -1,740 +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 "udis86wrapper.h"
-#include <boost/format.hpp>
-#include <boost/predef.h>
-#include <map>
-#pragma warning(push, 3)
-#include <asmjit/asmjit.h>
-#pragma warning(pop)
-#include "hooklib.h"
-#include "ttrampolinepool.h"
-#include "utility.h"
-#include <addrtools.h>
-#include <formatters.h>
-#include <shmlogger.h>
-#include <winapi.h>
-
-#if BOOST_ARCH_X86_64
-#pragma message("64bit build")
-#define JUMP_SIZE 13
-#elif BOOST_ARCH_X86_32
-#define JUMP_SIZE 5
-#else
-#error "unsupported architecture"
-#endif
-
-using namespace asmjit;
-// from here on out I'll only test for 64 or "other"
-
-using namespace HookLib;
-using namespace asmjit;
-
-using namespace usvfs;
-
-struct THookInfo
-{
- LPVOID originalFunction;
- LPVOID replacementFunction;
- LPVOID detour; // detour to call the original function after hook was installed.
- LPVOID trampoline; // code fragment that decides whether the replacement function or
- // detour is executed (preventing endless loops)
- std::vector<uint8_t>
- preamble; // part of the detour that needs to be re-inserted into the original
- // function to return it to vanilla state
- bool stub; // if this is true, the trampoline calls the "replacement"-function that
- // before the original function, not instead of it
- enum
- {
- TYPE_HOTPATCH, // official hot-patch variant as used on 32-bit windows
- TYPE_WIN64PATCH, // custom patch variant used on 64-bit windows
- TYPE_CHAINPATCH, // the hook is part of the hook chain (and not the first)
- TYPE_OVERWRITE, // full jump overwrite used if none of the above work
- TYPE_RIPINDIRECT // the function already started on a rip-relative jump so we only
- // modified that variable
- } type;
-};
-
-UDis86Wrapper& disasm()
-{
- static UDis86Wrapper instance;
- return instance;
-}
-
-void PauseOtherThreads()
-{
- // TODO: implement me!
-}
-
-void ResumePausedThreads()
-{
- // TODO: implement me! should resume only the threads paused by PauseOtherThreads
-}
-
-// not using the disassembler because this is simpler
-LPBYTE ShortJumpTarget(LPBYTE address)
-{
- int8_t off = *(address + 1);
- return address + 2 + off;
-}
-
-size_t GetJumpSize(LPBYTE, LPVOID)
-{
- // TODO: it would be neater to use asmjit to generate this jump and ask asmjit for
- // the size of this jump but with asmjit I can only generate absolute jumps, which
- // take too much space.
-
- // Since trampoline buffers is always allocated within 32-bit
- // address range of jump, we can say with confidence that a 5-byte jump is possible
- return 5;
-}
-
-void WriteLongJump(LPBYTE jumpAddr, LPVOID destination)
-{
- // TODO: not using asmjit here because I couldn't figure out how to generate
- // a working, space-optimized, relative jump to outside the generated code and
- // we do want to optimize this jump
-#if BOOST_ARCH_X86_64
- intptr_t dist = reinterpret_cast<intptr_t>(destination) -
- (reinterpret_cast<intptr_t>(jumpAddr) + 5);
- int32_t distShort = static_cast<int32_t>(dist);
-#else
- int32_t distShort = reinterpret_cast<intptr_t>(destination) -
- (reinterpret_cast<intptr_t>(jumpAddr) + 5);
-#endif
- *jumpAddr = 0xE9;
- *reinterpret_cast<int32_t*>(jumpAddr + 1) = distShort;
-}
-
-void WriteSingleJump(THookInfo& hookInfo, HookError* error)
-{
- DWORD oldprotect, ignore;
- // Set the target function to copy on write, so we don't modify code for other
- // processes
- if (!VirtualProtect(hookInfo.originalFunction, JUMP_SIZE, PAGE_EXECUTE_WRITECOPY,
- &oldprotect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- WriteLongJump(reinterpret_cast<LPBYTE>(hookInfo.originalFunction),
- hookInfo.trampoline);
-
- // restore old memory protection
- if (!VirtualProtect(hookInfo.originalFunction, JUMP_SIZE, oldprotect, &ignore)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- if (error != nullptr) {
- *error = ERR_NONE;
- }
-}
-
-void WriteShortJump(LPBYTE jumpAddr, const signed char offset)
-{
- *jumpAddr = 0xEB;
- *(jumpAddr + 1) = offset;
-}
-
-void WriteIndirectJump(THookInfo& hookInfo, size_t jumpSize, HookError* error)
-{
- DWORD oldProtect = 0;
- LPBYTE jumpAddr = reinterpret_cast<LPBYTE>(hookInfo.originalFunction) - jumpSize;
- // allow write to jump address + the short jump inside the function
- if (!VirtualProtect(jumpAddr, jumpSize + 2, PAGE_EXECUTE_WRITECOPY, &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- // insert the long jump first, then the short jump to the long jump, thus
- // activating the reroute
- WriteLongJump(jumpAddr, hookInfo.trampoline);
-
- PauseOtherThreads();
- WriteShortJump(jumpAddr + jumpSize, -(static_cast<int8_t>(jumpSize) + 2));
- ResumePausedThreads();
-
- // restore access protection
- if (!VirtualProtect(jumpAddr, jumpSize + 2, oldProtect, &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
- if (error != nullptr) {
- *error = ERR_NONE;
- }
-}
-
-/// implements function hooking using the mechanism intended for hot patching
-/// Explanation: the visual studio compiler offers an option to prepare functions
-/// for hot patching. In this case the compiler leaves room for one far jump before
-/// the actual function and a 2-byte nop inside the function.
-/// to hook such a function we write a jump to our replacement function to the
-/// space before the function and short jump to that jump to where the 2-byte nop
-/// was.
-/// On 32-bit windows, MS seems to have compiled all relevant functions in the
-/// windows API for hot patching starting with Windows XP SP3
-/// On 64-bit windows a lot of functions have the space for a jump before the function
-/// but they don't have the 2-byte nop so this function doesn't work
-/// \param hookInfo info about the hook to be installed
-/// \return true on success, false on error
-BOOL HookHotPatch(THookInfo& hookInfo, HookError* error)
-{
- LPVOID original = reinterpret_cast<LPVOID>(hookInfo.originalFunction);
-
- if (hookInfo.stub) {
- hookInfo.trampoline = TrampolinePool::instance().storeStub(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- shared::AddrAdd(original, 2));
- } else {
- hookInfo.trampoline = TrampolinePool::instance().storeTrampoline(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- shared::AddrAdd(original, 2));
- }
-
- WriteIndirectJump(hookInfo, JUMP_SIZE, error);
- hookInfo.type = THookInfo::TYPE_HOTPATCH;
- // in this case we don't need a separate detour, we simply jump past the 2-byte nop
- hookInfo.detour = reinterpret_cast<LPBYTE>(hookInfo.originalFunction) + 2;
-
- return TRUE;
-}
-
-uintptr_t followJumps(THookInfo& hookInfo)
-{
- LPBYTE original = reinterpret_cast<LPBYTE>(hookInfo.originalFunction);
- LPBYTE shortTarget = ShortJumpTarget(original);
-
- // disassemble the long jump
- disasm().setInputBuffer(shortTarget, JUMP_SIZE);
-
- ud_disassemble(disasm());
- if (ud_insn_mnemonic(disasm()) != UD_Ijmp) {
- // this shouldn't happen, we only call this if the jump was discovered before
- throw std::runtime_error("failed to find jump in patch");
- }
-
- uint64_t res = ud_insn_off(disasm()) + ud_insn_len(disasm());
- res += disasm().jumpOffset();
-
- return static_cast<uintptr_t>(res);
-}
-
-///
-/// \brief hook a call that is implemented as a short-jump to a long jump using a
-/// rip-relative address variable
-/// \param hookInfo info about the hook to be installed
-/// \param error if the return code is false and this is not null, the referred-to
-/// variable is set to an error code
-/// \return true on success, false on error
-///
-BOOL HookRIPIndirection(THookInfo& hookInfo, HookError* error)
-{
- uintptr_t res = followJumps(hookInfo);
-
- const ud_operand_t* op = ud_insn_opr(disasm(), 0);
-
- if (op->base != UD_R_RIP) {
- throw std::runtime_error("expected rip-relative addressing");
- }
-
- auto chainNext = disasm().jumpTarget();
- if (hookInfo.stub) {
- hookInfo.trampoline = TrampolinePool::instance().storeStub(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- reinterpret_cast<LPVOID>(chainNext));
- } else {
- hookInfo.trampoline = TrampolinePool::instance().storeTrampoline(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- reinterpret_cast<LPVOID>(chainNext));
- }
-
- DWORD oldProtect = 0;
- if (!VirtualProtect(reinterpret_cast<LPVOID>(res), 2, PAGE_EXECUTE_WRITECOPY,
- &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- *reinterpret_cast<uintptr_t*>(res) = reinterpret_cast<uintptr_t>(hookInfo.trampoline);
-
- if (!VirtualProtect(reinterpret_cast<LPVOID>(res), 2, oldProtect, &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- hookInfo.type = THookInfo::TYPE_RIPINDIRECT;
- hookInfo.detour = reinterpret_cast<LPVOID>(chainNext);
-
- if (error != nullptr) {
- *error = ERR_NONE;
- }
-
- return TRUE;
-}
-
-BOOL HookChainHook(THookInfo& hookInfo, LPBYTE jumpPos, HookError* error)
-{
- // disassemble the long jump
- disasm().setInputBuffer(jumpPos, JUMP_SIZE);
-
- ud_disassemble(disasm());
- if (ud_insn_mnemonic(disasm()) != UD_Ijmp) {
- // this shouldn't happen, we only call this if the jump was discovered before
- throw std::runtime_error("failed to find jump in patch");
- }
-
- auto chainTarget = disasm().jumpTarget();
-
- size_t size = ud_insn_len(disasm());
-
- // save the original code for the preamble so we can restore it later
- hookInfo.preamble.resize(size);
- memcpy(&hookInfo.preamble[0], jumpPos, size);
-
- spdlog::get("usvfs")->info("existing hook to {0:x} in {1}", chainTarget,
- shared::string_cast<std::string>(
- winapi::ex::wide::getSectionName((void*)chainTarget)));
-
- if (hookInfo.stub) {
- hookInfo.trampoline = TrampolinePool::instance().storeStub(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- reinterpret_cast<LPVOID>(chainTarget));
- } else {
- hookInfo.trampoline = TrampolinePool::instance().storeTrampoline(
- hookInfo.replacementFunction,
- reinterpret_cast<LPVOID>(hookInfo.originalFunction),
- reinterpret_cast<LPVOID>(chainTarget));
- }
-
- DWORD oldProtect = 0;
- if (!VirtualProtect(jumpPos, size, PAGE_EXECUTE_WRITECOPY, &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- WriteLongJump(jumpPos, hookInfo.trampoline);
-
- if (!VirtualProtect(jumpPos, size, oldProtect, &oldProtect)) {
- throw std::runtime_error("failed to change virtual protection");
- }
-
- hookInfo.type = THookInfo::TYPE_CHAINPATCH;
- hookInfo.detour = reinterpret_cast<LPVOID>(chainTarget);
-
- if (error != nullptr) {
- *error = ERR_NONE;
- }
-
- return TRUE;
-}
-
-///
-/// implements hooking by chaining to an existing hook
-/// \param hookInfo info about the hook to be installed
-/// \param error if the return code is false and this is not null,
-/// the referred-to variable is set to an error code
-/// \return true on success, false on error
-///
-BOOL HookChainHook(THookInfo& hookInfo, HookError* error)
-{
- return HookChainHook(hookInfo, reinterpret_cast<LPBYTE>(hookInfo.originalFunction),
- error);
-}
-
-///
-/// implements hooking by chaining to an existing hot patch
-/// \param hookInfo info about the hook to be installed
-/// \param error if the return code is false and this is not null,
-/// the referred-to variable is set to an error code
-/// \return true on success, false on error
-///
-BOOL HookChainPatch(THookInfo& hookInfo, HookError* error)
-{
- LPBYTE original = reinterpret_cast<LPBYTE>(hookInfo.originalFunction);
- LPBYTE shortTarget = ShortJumpTarget(original);
-
- return HookChainHook(hookInfo, shortTarget, error);
-}
-
-/// implements function hooking by overwriting the first n bytes of the function
-/// with a jump to the replacement function. Since this is destructive to the original
-/// function code the first n bytes of the function need to be copied somewhere else
-/// and that code needs to be called via a detour. This is a lot more complex than
-/// the hotpatch mechanism.
-/// \param hookInfo info about the hook to be installed
-/// \param error if the return code is false and this is not null, the referred-to
-/// variable is set to an error code
-/// \return true on success, false on error
-BOOL HookDisasm(THookInfo& hookInfo, HookError* error)
-{
- LPBYTE address = reinterpret_cast<LPBYTE>(hookInfo.originalFunction);
- ud_set_input_buffer(disasm(), address, 40);
-
- size_t jumpSize = GetJumpSize(
- static_cast<LPBYTE>(hookInfo.originalFunction),
- TrampolinePool::instance().currentBufferAddress(hookInfo.originalFunction));
-
- // test if we have room for a jump before the function
- bool jumpspace = true;
- for (size_t i = 0; i < jumpSize; ++i) {
- if (*(address - i - 1) != 0x90) {
- jumpspace = false;
- break;
- }
- }
-
- size_t minSize = jumpspace ? 2 : jumpSize;
-
- // iterate over all instructions that overlap with the jump instructions
- // we want to write.
- // TODO right now, this does not test if the function is smaller than the jump.
- size_t size = 0;
- while (size < minSize) {
- if (ud_disassemble(disasm()) == 0) {
- throw std::runtime_error("premature end of file in disassembly");
- }
-
- if ((size == 0) && (ud_insn_mnemonic(disasm()) == UD_Ijmp)) {
- if (error != nullptr) {
- *error = ERR_JUMP;
- }
- return FALSE;
- }
-
- size += ud_insn_len(disasm());
-
- // ret instruction or int3 smells like function end
- if ((ud_insn_mnemonic(disasm()) == UD_Iret) ||
- (ud_insn_mnemonic(disasm()) == UD_Iint3)) {
- if (error != nullptr) {
- *error = ERR_FUNCEND;
- }
- return FALSE;
- }
-
- // check the operands for relative addressing (not supported)
- for (int i = 0; i < 3; ++i) {
- const ud_operand* op = ud_insn_opr(disasm(), i);
- if (op) {
- if (
- // rip-relative are not handled, usually relative jumps
- op->base == UD_R_RIP
-
- // rsp-relative call are not handled (there are valid rsp-relative
- // instructions, e.g. sub)
- || (ud_insn_mnemonic(disasm()) == UD_Icall && op->base == UD_R_RSP)) {
- if (error != nullptr) {
- *error = ERR_RIP;
- }
- return FALSE;
- }
- }
- }
- }
-
- // save the original code for the preamble so we can restore it later
- hookInfo.preamble.resize(size);
- memcpy(&hookInfo.preamble[0], hookInfo.originalFunction, size);
-
- size_t rerouteOffset = 0;
- if (hookInfo.stub) {
- hookInfo.trampoline = TrampolinePool::instance().storeStub(
- hookInfo.replacementFunction, hookInfo.originalFunction, size, &rerouteOffset);
- } else {
- hookInfo.trampoline = TrampolinePool::instance().storeTrampoline(
- hookInfo.replacementFunction, hookInfo.originalFunction, size, &rerouteOffset);
- }
-
- if (jumpspace) {
- WriteIndirectJump(hookInfo, jumpSize, error);
- hookInfo.type = THookInfo::TYPE_WIN64PATCH;
- } else {
- WriteSingleJump(hookInfo, error);
- hookInfo.type = THookInfo::TYPE_OVERWRITE;
- }
- hookInfo.detour = reinterpret_cast<LPBYTE>(hookInfo.trampoline) + rerouteOffset;
-
- return TRUE;
-}
-
-enum EPreamble
-{
- PRE_PATCHFREE,
- PRE_PATCHUSED,
- PRE_RIPINDIRECT,
- PRE_FOREIGNHOOK,
- PRE_UNKNOWN
-};
-
-EPreamble DeterminePreamble(LPBYTE address)
-{
- ud_set_input_buffer(disasm(), address, JUMP_SIZE);
- ud_disassemble(disasm());
-
- if ((ud_insn_mnemonic(disasm()) == UD_Imov) &&
- (ud_insn_opr(disasm(), 0) == ud_insn_opr(disasm(), 1)) &&
- (ud_insn_opr(disasm(), 0)->type == UD_OP_REG)) {
- // mov edi, edi
- return PRE_PATCHFREE;
- } else if ((ud_insn_mnemonic(disasm()) == UD_Ijmp) && (ud_insn_len(disasm()) == 2)) {
- // determine target of the short jump
- LPBYTE shortTarget = ShortJumpTarget(address);
-
- // test if that short jump leads to a long jump
- ud_set_input_buffer(disasm(), shortTarget, JUMP_SIZE);
- ud_disassemble(disasm());
- if (ud_insn_mnemonic(disasm()) == UD_Ijmp) {
- const ud_operand* op = ud_insn_opr(disasm(), 0);
- if (op->base == UD_R_RIP) {
- return PRE_RIPINDIRECT;
- } else {
- return PRE_PATCHUSED;
- }
- } else {
- return PRE_UNKNOWN;
- }
- } else if (ud_insn_mnemonic(disasm()) == UD_Ijmp) {
- return PRE_FOREIGNHOOK;
- } else {
- return PRE_UNKNOWN;
- }
-}
-
-static std::map<HOOKHANDLE, THookInfo> s_Hooks;
-
-static HOOKHANDLE GenerateHandle()
-{
- static ULONG NextHandle = 1;
- return NextHandle++;
-}
-
-HOOKHANDLE applyHook(THookInfo info, HookError* error)
-{
- // apply the correct hook function depending on how the function start looks
- EPreamble preamble = DeterminePreamble((LPBYTE)info.originalFunction);
-
- BOOL success = FALSE;
- switch (preamble) {
- case PRE_PATCHUSED: {
- success = HookChainPatch(info, error);
- } break;
- case PRE_PATCHFREE: {
- success = HookHotPatch(info, error);
- } break;
- case PRE_RIPINDIRECT: {
- success = HookRIPIndirection(info, error);
- } break;
- case PRE_FOREIGNHOOK: {
- success = HookChainHook(info, error);
- } break;
- default: {
- success = HookDisasm(info, error);
- } break;
- }
-
- if (success == TRUE) {
- HOOKHANDLE handle = GenerateHandle();
- s_Hooks[handle] = info;
- return handle;
- } else {
- return INVALID_HOOK;
- }
-}
-
-HOOKHANDLE HookLib::InstallStub(LPVOID functionAddress, LPVOID stubAddress,
- HookError* error)
-{
- if (functionAddress == nullptr) {
- if (error != nullptr)
- *error = ERR_INVALIDPARAMETERS;
- return INVALID_HOOK;
- }
-
- THookInfo info;
- info.originalFunction = functionAddress;
- info.replacementFunction = stubAddress;
- info.stub = true;
- info.detour = nullptr;
- info.trampoline = nullptr;
- info.type = THookInfo::TYPE_OVERWRITE;
-
- return applyHook(info, error);
-}
-
-HOOKHANDLE HookLib::InstallStub(HMODULE module, LPCSTR functionName, LPVOID stubAddress,
- HookError* error)
-{
- LPVOID funcAddr = MyGetProcAddress(module, functionName);
- return InstallStub(funcAddr, stubAddress, error);
-}
-
-HOOKHANDLE HookLib::InstallHook(LPVOID functionAddress, LPVOID hookAddress,
- HookError* error)
-{
- if (functionAddress == nullptr) {
- if (error != nullptr)
- *error = ERR_INVALIDPARAMETERS;
- return INVALID_HOOK;
- }
- THookInfo info;
- info.originalFunction = functionAddress;
- info.replacementFunction = hookAddress;
- info.stub = false;
- info.detour = nullptr;
- info.trampoline = nullptr;
- info.type = THookInfo::TYPE_OVERWRITE;
-
- return applyHook(info, error);
-}
-
-HOOKHANDLE HookLib::InstallHook(HMODULE module, LPCSTR functionName, LPVOID hookAddress,
- HookError* error)
-{
- LPVOID funcAddr = MyGetProcAddress(module, functionName);
- return InstallHook(funcAddr, hookAddress, error);
-}
-
-void HookLib::RemoveHook(HOOKHANDLE handle)
-{
- auto iter = s_Hooks.find(handle);
- if (iter != s_Hooks.end()) {
- THookInfo info = iter->second;
- PauseOtherThreads();
- LPBYTE address = reinterpret_cast<LPBYTE>(info.originalFunction);
- if (info.type == THookInfo::TYPE_HOTPATCH) {
- // return the short jump to 2-byte nop
- // TODO: This doesn't take into account if we chain-loaded another hook
-
- DWORD oldProtect = 0;
- if (!VirtualProtect(address, 2, PAGE_EXECUTE_WRITECOPY, &oldProtect)) {
- throw shared::windows_error("failed to gain write access to remove hook");
- }
- *address = 0x8b;
- *(address + 1) = 0xff;
- VirtualProtect(address, 2, oldProtect, &oldProtect);
- } else if ((info.type == THookInfo::TYPE_OVERWRITE) ||
- (info.type == THookInfo::TYPE_WIN64PATCH)) {
- DWORD oldProtect = 0;
- if (!VirtualProtect(address, info.preamble.size(), PAGE_EXECUTE_WRITECOPY,
- &oldProtect)) {
- throw shared::windows_error("failed to gain write access to remove hook");
- }
- // TODO: remove hook by restoring the original function. This only works if we
- // have the exact code available somewhere
- memcpy(address, &info.preamble[0], info.preamble.size());
- VirtualProtect(address, info.preamble.size(), oldProtect, &oldProtect);
- } else if (info.type == THookInfo::TYPE_CHAINPATCH) {
- // we could attempt to restore the original function preamble but I'm not
- // sure we can reliably write the jump with same (or lower) size.
- // Instead overwrite our own trampoline
- disasm().setInputBuffer(static_cast<uint8_t*>(info.originalFunction), JUMP_SIZE);
- ud_disassemble(disasm());
- if (ud_insn_mnemonic(disasm()) != UD_Ijmp) {
- // this shouldn't happen, we only call this if the jump was discovered before
- throw std::runtime_error("failed to find jump in patch");
- }
-
- LPBYTE jumpPos = static_cast<LPBYTE>(info.originalFunction);
- if (ud_insn_len(disasm()) == 2) {
- jumpPos = reinterpret_cast<LPBYTE>(disasm().jumpTarget());
- }
-
- DWORD oldProtect = 0;
- if (!VirtualProtect(jumpPos, info.preamble.size(), PAGE_EXECUTE_WRITECOPY,
- &oldProtect)) {
- throw shared::windows_error("failed to gain write access to remove hook");
- }
- memcpy(jumpPos, info.preamble.data(), info.preamble.size());
- VirtualProtect(jumpPos, info.preamble.size(), oldProtect, &oldProtect);
- } else if (info.type == THookInfo::TYPE_RIPINDIRECT) {
- uintptr_t res = followJumps(info);
- DWORD oldProtect = 0;
- if (!VirtualProtect(reinterpret_cast<LPVOID>(res), JUMP_SIZE,
- PAGE_EXECUTE_WRITECOPY, &oldProtect)) {
- throw shared::windows_error("failed to gain write access to remove hook");
- }
- *reinterpret_cast<uintptr_t*>(res) =
- reinterpret_cast<uintptr_t>(info.originalFunction);
- VirtualProtect(reinterpret_cast<LPVOID>(res), JUMP_SIZE, oldProtect, &oldProtect);
- } else {
- spdlog::get("usvfs")->critical("can't remove hook, unknown hook type!");
- }
-
- s_Hooks.erase(iter);
- ResumePausedThreads();
- } else {
- spdlog::get("usvfs")->info("handle unknown: {0:x}", handle);
- }
-}
-
-const char* HookLib::GetErrorString(HookError err)
-{
- switch (err) {
- case ERR_NONE:
- return "No Error";
- case ERR_INVALIDPARAMETERS:
- return "Invalid parameters";
- case ERR_FUNCEND:
- return "Function too short";
- case ERR_JUMP:
- return "Function starts on a jump";
- case ERR_RIP:
- return "RIP-relative addressing can't be relocated.";
- case ERR_RELJUMP:
- return "Relative Jump can't be relocated.";
- default:
- return "Unkown error code";
- }
-}
-
-const char* HookLib::GetHookType(HOOKHANDLE handle)
-{
- auto iter = s_Hooks.find(handle);
- if (iter != s_Hooks.end()) {
- THookInfo info = iter->second;
- switch (info.type) {
- case THookInfo::TYPE_HOTPATCH:
- return "hot patch";
- case THookInfo::TYPE_WIN64PATCH:
- return "64-bit hot patch";
- case THookInfo::TYPE_CHAINPATCH:
- return "chained patch";
- case THookInfo::TYPE_OVERWRITE:
- return "overwrite";
- case THookInfo::TYPE_RIPINDIRECT:
- return "rip indirection modified";
- default: {
- spdlog::get("usvfs")->error("invalid hook type {0}", info.type);
- return "invalid hook type";
- }
- }
- }
- return "invalid handle";
-}
-
-LPVOID HookLib::GetDetour(HOOKHANDLE handle)
-{
- auto iter = s_Hooks.find(handle);
- if (iter != s_Hooks.end()) {
- THookInfo info = iter->second;
- return info.detour;
- }
- return nullptr;
-}
diff --git a/libs/usvfs/src/thooklib/hooklib.h b/libs/usvfs/src/thooklib/hooklib.h
deleted file mode 100644
index a549c03..0000000
--- a/libs/usvfs/src/thooklib/hooklib.h
+++ /dev/null
@@ -1,125 +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 <map>
-#include <windows_sane.h>
-
-namespace HookLib
-{
-
-enum HookError
-{
- ERR_NONE,
- ERR_INVALIDPARAMETERS, // parameters are invalid
- ERR_FUNCEND, // function is too short to be hooked
- ERR_JUMP, // function consists only of an unconditional jump. Maybe it has already
- // been hooked?
- ERR_RIP, // segment of the function to be overwritten contains a instruction-relative
- // operation
- ERR_RELJUMP // segment of the function to be overwritten contains a relative jump we
- // can't relocated
-};
-
-typedef ULONG HOOKHANDLE;
-static const HOOKHANDLE INVALID_HOOK = (HOOKHANDLE)-1;
-
-///
-/// \brief install a stub (function to be called before the target function)
-/// \param functionAddress address of the function to stub
-/// \param stubAddress address of the stub function. This function has to have the
-/// signature of void foobar(LPVOID address).
-/// address receives the address of the function.
-/// \param error (optional) if set, the referenced variable will receive an error code
-/// describing the problem (if any)
-/// \return a handle to reference the hook in later operations or INVALID_HOOK on error
-///
-HOOKHANDLE InstallStub(LPVOID functionAddress, LPVOID stubAddress,
- HookError* error = nullptr);
-
-///
-/// \brief install a stub (function to be called before the target function)
-/// \param module the module containing the function to hook
-/// \param functionName name of the function to stub (as exported by the library)
-/// \param stubAddress address of the stub function. This function has to have the
-/// signature of void foobar(LPVOID address).
-/// address receives the address of the function.
-/// \param error (optional) if set, the referenced variable will receive an error code
-/// describing the problem (if any)
-/// \return a handle to reference the hook in later operations or INVALID_HOOK on error
-///
-HOOKHANDLE InstallStub(HMODULE module, LPCSTR functionName, LPVOID stubAddress,
- HookError* error = nullptr);
-
-///
-/// \brief install a hook (function replacing the existing functionality of the
-/// function)
-/// \param functionAddress address of the function to hook
-/// \param hookAddress address of the replacement function. This function has to have
-/// the exact same signature as the replaced function
-/// \param error (optional) if set, the referenced variable will receive an error code
-/// describing the problem (if any)
-/// \return a handle to reference the hook in later operations or INVALID_HOOK on error
-///
-HOOKHANDLE InstallHook(LPVOID functionAddress, LPVOID hookAddress,
- HookError* error = nullptr);
-
-///
-/// \brief install a hook (function replacing the existing functionality of the
-/// function)
-/// \param functionName name of the function to hook (as exported by the library)
-/// \param hookAddress address of the replacement function. This function has to have
-/// the exact same signature as the replaced function
-/// \param error (optional) if set, the referenced variable will receive an error code
-/// describing the problem (if any)
-/// \return a handle to reference the hook in later operations or INVALID_HOOK on error
-///
-HOOKHANDLE InstallHook(HMODULE module, LPCSTR functionName, LPVOID hookAddress,
- HookError* error = nullptr);
-
-///
-/// \brief remove a hook
-/// \param handle handle returned in InstallStub or InstallHook
-///
-void RemoveHook(HOOKHANDLE handle);
-
-///
-/// \brief determine the type of a hook
-/// \param handle the handle to look up
-/// \return a string describing the used hooking mechanism
-///
-const char* GetHookType(HOOKHANDLE handle);
-
-///
-/// \brief retrieve the address that can be used to directly call a detour
-/// \param handle handle for the hook
-/// \return function address
-///
-LPVOID GetDetour(HOOKHANDLE handle);
-
-///
-/// \brief resolve an error code to a descriptive string
-/// \param err the error code to resolve
-/// \return the error string
-///
-const char* GetErrorString(HookError err);
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/pch.cpp b/libs/usvfs/src/thooklib/pch.cpp
deleted file mode 100644
index 1d9f38c..0000000
--- a/libs/usvfs/src/thooklib/pch.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "pch.h"
diff --git a/libs/usvfs/src/thooklib/ttrampolinepool.cpp b/libs/usvfs/src/thooklib/ttrampolinepool.cpp
deleted file mode 100644
index 3b6a51f..0000000
--- a/libs/usvfs/src/thooklib/ttrampolinepool.cpp
+++ /dev/null
@@ -1,601 +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 "ttrampolinepool.h"
-#include <addrtools.h>
-#include <shmlogger.h>
-// #include <boost/thread/lock_guard.hpp>
-#include "udis86wrapper.h"
-
-using namespace asmjit;
-#if BOOST_ARCH_X86_64
-using namespace x86;
-#elif BOOST_ARCH_X86_32
-using namespace asmjit::x86;
-#endif
-
-using namespace usvfs::shared;
-
-namespace HookLib
-{
-
-TrampolinePool* TrampolinePool::s_Instance = nullptr;
-
-TrampolinePool::TrampolinePool() : m_MaxTrampolineSize(sizeof(LPVOID))
-{
- m_BarrierAddr = &TrampolinePool::barrier;
- m_ReleaseAddr = &TrampolinePool::release;
-
- SYSTEM_INFO sysInfo;
- ::ZeroMemory(&sysInfo, sizeof(SYSTEM_INFO));
- GetSystemInfo(&sysInfo);
- m_BufferSize = sysInfo.dwPageSize;
-
- // if search range = ffffff then addressmask = ffffffffff000000
- // => all jumps between xxxxxxxxxx000000 and xxxxxxxxxxffffff will use the same buffer
- // for trampolines which is guaranteed to be in that range
- // TODO it should be valid to use 2 ^ 32 as the search range to increase our chances
- // of finding a memory block we can reserve but then there is a problem with
- // converting negative jump distances to 32 bit I didn't understand. Everything
- // up to 2 ^ 31 seems to be fine though
- m_SearchRange = static_cast<size_t>(pow(2, 30)) - 1;
- m_AddressMask = std::numeric_limits<uint64_t>::max() - m_SearchRange;
-}
-
-// static
-void TrampolinePool::initialize()
-{
- if (!s_Instance)
- s_Instance = new TrampolinePool();
-}
-
-void TrampolinePool::setBlock(bool block)
-{
- m_FullBlock = block;
- if (m_ThreadGuards.get() == nullptr) {
- m_ThreadGuards.reset(new TThreadMap());
- }
-}
-
-#if BOOST_ARCH_X86_64
-// push all registers (except rax) and flags to the stack
-static void pushAll(X86Assembler& assembler)
-{
- assembler.pushf();
- assembler.push(rcx);
- assembler.push(rdx);
- assembler.push(rbx);
- assembler.push(rbp);
- assembler.push(rsi);
- assembler.push(rdi);
- assembler.push(r8);
- assembler.push(r9);
- assembler.push(r10);
- assembler.push(r11);
- assembler.push(r12);
- assembler.push(r13);
- assembler.push(r14);
- assembler.push(r15);
-}
-
-// pop all registers (except rax) and flags from stack
-static void popAll(X86Assembler& assembler)
-{
- assembler.pop(r15);
- assembler.pop(r14);
- assembler.pop(r13);
- assembler.pop(r12);
- assembler.pop(r11);
- assembler.pop(r10);
- assembler.pop(r9);
- assembler.pop(r8);
- assembler.pop(rdi);
- assembler.pop(rsi);
- assembler.pop(rbp);
- assembler.pop(rbx);
- assembler.pop(rdx);
- assembler.pop(rcx);
- assembler.popf();
-}
-#endif // BOOST_ARCH_X86_64
-
-void TrampolinePool::addBarrier(LPVOID rerouteAddr, LPVOID original,
- X86Assembler& assembler)
-{
- Label skipLabel = assembler.newLabel();
-
-#if BOOST_ARCH_X86_64
- pushAll(assembler);
- assembler.mov(rcx,
- imm(reinterpret_cast<int64_t>(
- original))); // set call parameter for call to barrier function
- assembler.mov(rax, imm((intptr_t)(void*)barrier));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
- popAll(assembler);
- // test barrier
- assembler.cmp(rax, 0); // test if the barrier is locked
- assembler.jz(skipLabel); // skip if barrier was locked
-
- // call replacement function
- // for this call no registers are saved. the called function is a compiled function so
- // it should correctly save non-volatile registers, and the caller can't expect the
- // volatile ones to remain valid
- assembler.pop(r10);
- assembler.mov(dword_ptr(rax), r10); // store that return address to the variable
- // supplied by the barrier function
- assembler.mov(rax, imm((intptr_t)(LPVOID)rerouteAddr));
- assembler.call(rax);
- assembler.push(rax); // save away result
-
- // open the barrier again
- pushAll(assembler);
- assembler.mov(rcx, imm(reinterpret_cast<int64_t>(original)));
- assembler.mov(rax, imm((intptr_t)(void*)release));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
- popAll(assembler);
- assembler.pop(r10); // get the result from the replacement function to a register
- assembler.push(rax); // push the original return address back on the stack
- assembler.mov(rax, r10); // move result of actual call to rax
- assembler.ret(); // return, using the original return address
-#else // BOOST_ARCH_X86_64
- assembler.push(imm(void_ptr_cast<int32_t>(
- original))); // push original function, as parameter to barrier
- assembler.mov(ecx, (Ptr) static_cast<void*>(TrampolinePool::barrier));
- assembler.call(ecx); // call barrier function
- assembler.cmp(eax, 0);
- assembler.jz(skipLabel); // if barrier is locked, jump to end of function
-
- // case a: we got through the barrier
- assembler.pop(ecx); // pop the return address into ecx
- assembler.mov(dword_ptr(eax), ecx); // store that return address in the variable
- // supplied by the barrier function
-
- assembler.mov(eax, (Ptr) static_cast<void*>(rerouteAddr));
- assembler.call(eax); // call replacement function (pointer was stored in front of the
- // trampoline) (this function gets the parameters that were on
- // the stack already and cleans
- // them up itself (stdcall convention))
- assembler.push(eax); // save away result
- assembler.push(imm(void_ptr_cast<int32_t>(original))); // open the barrier again
- assembler.mov(eax, (Ptr) static_cast<void*>(TrampolinePool::release));
- assembler.call(eax);
- assembler.pop(ecx); // pop the result from the actual call to ecx
- assembler.push(eax); // push the original return address (returned by
- // TTrampolinePool::release) back on the stack
- assembler.mov(eax, ecx); // move result of actual call to eax
- assembler.ret(); // return, using the original return address
-#endif // BOOST_ARCH_X86_64
-
- assembler.bind(skipLabel);
-}
-
-LPVOID TrampolinePool::roundAddress(LPVOID address) const
-{
- return reinterpret_cast<LPVOID>(reinterpret_cast<intptr_t>(address) & m_AddressMask);
-}
-
-TrampolinePool::BufferList& TrampolinePool::getBufferList(LPVOID address)
-{
- LPVOID rounded = roundAddress(address);
- auto iter = m_Buffers.find(rounded);
- if (iter == m_Buffers.end()) {
- BufferList newBufList = {0, std::vector<LPVOID>()};
- m_Buffers[rounded] = newBufList;
- iter = allocateBuffer(address);
- }
- return iter->second;
-}
-
-LPVOID TrampolinePool::storeStub(LPVOID reroute, LPVOID original, LPVOID returnAddress)
-{
- BufferList& bufferList = getBufferList(original);
- // first test to increase likelyhood we don't have to reallocate later
- if (bufferList.offset + m_MaxTrampolineSize > m_BufferSize) {
- allocateBuffer(original);
- }
-
- LPVOID spot = AddrAdd(*bufferList.buffers.rbegin(), bufferList.offset);
-
- // ??? write address of reroute to trampoline and move past the address
- *reinterpret_cast<LPVOID*>(spot) = reroute;
- // coverity[suspicious_sizeof]
- spot = AddrAdd(spot, sizeof(LPVOID));
- bufferList.offset += sizeof(LPVOID);
-
- JitRuntime runtime;
-#if BOOST_ARCH_X86_64
- X86Assembler assembler(&runtime);
-#else
- X86Assembler assembler(&runtime);
-#endif
- addCallToStub(assembler, original, reroute);
- addAbsoluteJump(assembler, reinterpret_cast<uint64_t>(returnAddress));
-
- size_t codeSize = assembler.getCodeSize();
-
- m_MaxTrampolineSize =
- std::max(m_MaxTrampolineSize, static_cast<int>(codeSize + sizeof(LPVOID)));
-
- // final test to see if we can store the trampoline in the buffer
- if ((bufferList.offset + codeSize) > m_BufferSize) {
- // can't place function in buffer, allocate another and try again
- allocateBuffer(original);
- // we could relocate the code and the data but this is simpler
- return storeStub(reroute, original, returnAddress);
- }
-
- // adjust relative jumps for move to buffer
- codeSize = assembler.relocCode(spot);
-
- uint8_t* code = assembler.getBuffer();
- memcpy(spot, code, codeSize);
-
- bufferList.offset += codeSize;
-
- return spot;
-}
-
-LPVOID TrampolinePool::storeTrampoline(LPVOID reroute, LPVOID original,
- LPVOID returnAddress)
-{
- BufferList& bufferList = getBufferList(original);
- // first test to increase likelyhood we don't have to reallocate later
- if (bufferList.offset + m_MaxTrampolineSize > m_BufferSize) {
- allocateBuffer(original);
- }
-
- LPVOID spot = AddrAdd(*bufferList.buffers.rbegin(), bufferList.offset);
-
- *reinterpret_cast<LPVOID*>(spot) = reroute;
- // coverity[suspicious_sizeof]
- spot = AddrAdd(spot, sizeof(LPVOID));
- bufferList.offset += sizeof(LPVOID);
-
- JitRuntime runtime;
- X86Assembler assembler(&runtime);
- addBarrier(reroute, original, assembler);
-#if BOOST_ARCH_X86_64
- assembler.mov(rax, imm((intptr_t)(void*)(returnAddress)));
- assembler.jmp(rax);
-#else
- assembler.mov(eax, imm((intptr_t)(void*)(returnAddress)));
- assembler.jmp(eax);
-#endif
- size_t codeSize = assembler.getCodeSize();
-
- m_MaxTrampolineSize =
- std::max(m_MaxTrampolineSize, static_cast<int>(codeSize + sizeof(LPVOID)));
-
- // final test to see if we can store the trampoline in the buffer
- if ((bufferList.offset + codeSize) > m_BufferSize) {
- // can't place function in buffer, allocate another and try again
- allocateBuffer(original);
- // we could relocate the code and the data but this is simpler
- return storeTrampoline(reroute, original, returnAddress);
- }
-
- // adjust relative jumps for move to buffer
- codeSize = assembler.relocCode(spot);
-
- // copy code to buffer
- uint8_t* code = assembler.getBuffer();
- memcpy(spot, code, codeSize);
-
- bufferList.offset += codeSize;
- return spot;
-}
-
-#if BOOST_ARCH_X86_64
-void TrampolinePool::copyCode(X86Assembler& assembler, LPVOID source, size_t numBytes)
-{
- static UDis86Wrapper disasm;
-
- disasm.setInputBuffer(static_cast<const uint8_t*>(source), numBytes);
-
- size_t offset = 0;
-
- while (ud_disassemble(disasm) != 0) {
- // rewrite relative jumps, blind copy everything else
-
- offset += ud_insn_len(disasm);
-
- // WARNING: doesn't support conditional jumps
- if ((ud_insn_mnemonic(disasm) == UD_Ijmp) &&
- (ud_insn_opr(disasm, 0)->type == UD_OP_JIMM)) {
- uintptr_t dest = disasm.jumpTarget();
- assembler.mov(rax, imm(static_cast<uint64_t>(dest)));
- assembler.jmp(rax);
- } else {
- assembler.embed(ud_insn_ptr(&disasm.obj()), ud_insn_len(&disasm.obj()));
- // assembler.data();
- }
- }
-}
-#endif
-
-void TrampolinePool::addCallToStub(X86Assembler& assembler, LPVOID original,
- LPVOID reroute)
-{
-#if BOOST_ARCH_X86_64
- pushAll(assembler);
- assembler.mov(rcx, imm(reinterpret_cast<int64_t>(original)));
- assembler.mov(rax, imm((intptr_t)(LPVOID)reroute));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
- popAll(assembler);
-#else // BOOST_ARCH_X86_64
- assembler.push(reinterpret_cast<int64_t>(original));
- assembler.mov(ecx, imm((intptr_t)(LPVOID)reroute));
- assembler.call(ecx);
- assembler.pop(ecx); // remove argument from stack
-#endif // BOOST_ARCH_X86_64
-}
-
-void TrampolinePool::addAbsoluteJump(X86Assembler& assembler, uint64_t destination)
-{
-#if BOOST_ARCH_X86_64
- assembler.push(rax);
- assembler.push(rax);
- assembler.mov(rax, imm(destination));
- assembler.mov(ptr(rsp, 8), rax);
- assembler.pop(rax);
- assembler.ret();
-#else // BOOST_ARCH_X86_64
- assembler.push(imm(destination));
- assembler.ret();
-#endif // BOOST_ARCH_X86_64
-}
-
-LPVOID TrampolinePool::storeStub(LPVOID reroute, LPVOID original, size_t preambleSize,
- size_t* rerouteOffset)
-{
- BufferList& bufferList = getBufferList(original);
- // first test to increase likelyhood we don't have to reallocate later
- if (bufferList.offset + m_MaxTrampolineSize > m_BufferSize) {
- allocateBuffer(original);
- }
-
- LPVOID spot = AddrAdd(*bufferList.buffers.rbegin(), bufferList.offset);
-
- *reinterpret_cast<LPVOID*>(spot) = reroute;
- // coverity[suspicious_sizeof]
- spot = AddrAdd(spot, sizeof(LPVOID));
- bufferList.offset += sizeof(LPVOID);
-
- JitRuntime runtime;
- X86Assembler assembler(&runtime);
- addCallToStub(assembler, original, reroute);
-#if BOOST_ARCH_X86_64
- // insert backup code
- *rerouteOffset = assembler.getCodeSize();
- copyCode(assembler, original, preambleSize);
-#else // BOOST_ARCH_X86_64
- assembler.embed(original, preambleSize);
-#endif // BOOST_ARCH_X86_64
- addAbsoluteJump(assembler, reinterpret_cast<uint64_t>(original) + preambleSize);
-
- // adjust relative jumps for move to buffer
- size_t codeSize = assembler.getCodeSize();
-
- m_MaxTrampolineSize =
- std::max(m_MaxTrampolineSize, static_cast<int>(codeSize + sizeof(LPVOID)));
-
- // final test to see if we can store the trampoline in the buffer
- if ((bufferList.offset + codeSize) > m_BufferSize) {
- // can't place function in buffer, allocate another and try again
- allocateBuffer(original);
- // we could relocate the code and the data but this is simpler
- return storeStub(reroute, original, preambleSize, rerouteOffset);
- }
-
- // copy code to buffer
- codeSize = assembler.relocCode(spot);
-
- bufferList.offset += preambleSize + codeSize;
- return spot;
-}
-
-LPVOID TrampolinePool::storeTrampoline(LPVOID reroute, LPVOID original,
- size_t preambleSize, size_t* rerouteOffset)
-{
- BufferList& bufferList = getBufferList(original);
- // first test to increase likelyhood we don't have to reallocate later
- if (bufferList.offset + m_MaxTrampolineSize > m_BufferSize) {
- allocateBuffer(original);
- }
-
- LPVOID spot = AddrAdd(*bufferList.buffers.rbegin(), bufferList.offset);
-
- *reinterpret_cast<LPVOID*>(spot) = reroute;
- // coverity[suspicious_sizeof]
- spot = AddrAdd(spot, sizeof(LPVOID));
- bufferList.offset += sizeof(LPVOID);
-
- JitRuntime runtime;
- X86Assembler assembler(&runtime);
- addBarrier(reroute, original, assembler);
- // insert backup code
- *rerouteOffset = assembler.getCodeSize();
- assembler.embed(original, static_cast<uint32_t>(preambleSize));
- addAbsoluteJump(assembler, reinterpret_cast<uint64_t>(original) + preambleSize);
-
- // adjust relative jumps for move to buffer
- size_t codeSize = assembler.getCodeSize();
-
- m_MaxTrampolineSize =
- std::max(m_MaxTrampolineSize, static_cast<int>(codeSize + sizeof(LPVOID)));
-
- // TODO this does not take into account that the code size may technically change
- // after relocation in which case the following test may determine the code fits into
- // the buffer when it really doesnt't. asmjit doesn't seem to provide a way to adjust
- // jumps without actually moving the code though
-
- // final test to see if we can store the trampoline in the buffer
- if ((bufferList.offset + codeSize) > m_BufferSize) {
- // can't place function in buffer, allocate another and try again
- allocateBuffer(original);
- // we could relocate the code and the data but this is simpler
- return storeTrampoline(reroute, original, preambleSize, rerouteOffset);
- }
-
- // copy code to buffer
- codeSize = static_cast<size_t>(assembler.relocCode(spot));
-
- bufferList.offset += preambleSize + codeSize;
-
- return spot;
-}
-
-LPVOID TrampolinePool::currentBufferAddress(LPVOID addressNear)
-{
- LPVOID rounded = roundAddress(addressNear);
- auto lookupAddress = m_Buffers.find(rounded);
-
- if (lookupAddress == m_Buffers.end()) {
- lookupAddress = m_Buffers.insert(std::make_pair(rounded, BufferList())).first;
- }
- if (lookupAddress->second.buffers.size() == 0) {
- allocateBuffer(addressNear);
- }
-
- LPVOID res = *(lookupAddress->second.buffers.rbegin());
- return res;
-}
-
-void TrampolinePool::forceUnlockBarrier()
-{
- if (m_ThreadGuards.get() != nullptr) {
- for (auto funcId : *m_ThreadGuards) {
- (*m_ThreadGuards)[funcId.first] = nullptr;
- }
- } // else no barriers to unlock
-}
-
-TrampolinePool::BufferMap::iterator TrampolinePool::allocateBuffer(LPVOID addressNear)
-{
- // allocate a buffer that we can write to and that is executable
- SYSTEM_INFO sysInfo;
- ::ZeroMemory(&sysInfo, sizeof(SYSTEM_INFO));
- GetSystemInfo(&sysInfo);
-
- LPVOID rounded = roundAddress(addressNear);
- auto iter = m_Buffers.find(rounded);
- uintptr_t lowerEnd = reinterpret_cast<uintptr_t>(rounded);
- if (iter->second.buffers.size() > 0) {
- // start searching were we last found a buffer
- lowerEnd = reinterpret_cast<uintptr_t>(*iter->second.buffers.rbegin()) +
- sysInfo.dwPageSize;
- }
-
- uintptr_t start =
- std::max(std::max(lowerEnd, MIN_ALLOC_ADDR),
- reinterpret_cast<uintptr_t>(sysInfo.lpMinimumApplicationAddress));
- uintptr_t upperEnd = reinterpret_cast<uintptr_t>(rounded) + m_SearchRange;
- uintptr_t end = std::min(
- upperEnd, reinterpret_cast<uintptr_t>(sysInfo.lpMaximumApplicationAddress));
-
- LPVOID buffer = nullptr;
- for (uintptr_t cur = start; cur < end; cur += sysInfo.dwPageSize) {
- buffer = VirtualAlloc(reinterpret_cast<LPVOID>(cur), m_BufferSize,
- MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
- if (buffer != nullptr) {
- break;
- }
- }
- if (buffer == nullptr) {
- throw std::runtime_error("failed to allocate buffer in range");
- }
-
- // the caller must have looked up the bufferlist in order to determine that a
- // buffer has to be allocated
- assert(iter != m_Buffers.end());
-
- iter->second.offset = 0;
- iter->second.buffers.push_back(buffer);
- spdlog::get("usvfs")->debug(
- "allocated trampoline buffer for jumps between {0:p} and {1:x} at {2:p}"
- "(size {3})",
- rounded, (reinterpret_cast<uintptr_t>(rounded) + m_SearchRange), buffer,
- m_BufferSize);
- return iter;
-}
-
-LPVOID TrampolinePool::barrier(LPVOID function)
-{
- DWORD err = GetLastError();
- LPVOID res = instance().barrierInt(function);
- SetLastError(err);
- return res;
-}
-
-LPVOID TrampolinePool::release(LPVOID function)
-{
- DWORD err = GetLastError();
- LPVOID res = instance().releaseInt(function);
- SetLastError(err);
- return res;
-}
-
-LPVOID TrampolinePool::barrierInt(LPVOID func)
-{
- if (m_FullBlock) {
- return nullptr;
- }
-
- if (m_ThreadGuards.get() == nullptr) {
- m_ThreadGuards.reset(new TThreadMap());
- }
-
- auto iter = m_ThreadGuards->find(func);
- if ((iter == m_ThreadGuards->end()) || (iter->second == nullptr)) {
- (*m_ThreadGuards)[func] = reinterpret_cast<LPVOID>(1);
- return &(*m_ThreadGuards)[func];
- } else {
- return nullptr;
- }
-}
-
-LPVOID TrampolinePool::releaseInt(LPVOID func)
-{
- DWORD lastError = GetLastError();
- if (m_ThreadGuards.get() == nullptr) {
- m_ThreadGuards.reset(new TThreadMap());
- }
-
- auto iter = m_ThreadGuards->find(func);
- if (iter == m_ThreadGuards->end()) {
- spdlog::get("hooks")->error("failed to release barrier for func {}", func);
- ::SetLastError(lastError);
- return nullptr;
- }
-
- LPVOID res = (*m_ThreadGuards)[func];
- (*m_ThreadGuards)[func] = nullptr;
-
- ::SetLastError(lastError);
- return res;
-}
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/ttrampolinepool.h b/libs/usvfs/src/thooklib/ttrampolinepool.h
deleted file mode 100644
index 5b44c51..0000000
--- a/libs/usvfs/src/thooklib/ttrampolinepool.h
+++ /dev/null
@@ -1,222 +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 "asmjit_sane.h"
-#include <logging.h>
-#include <map>
-#include <vector>
-#include <windows_sane.h>
-
-#ifdef _MSC_VER
-#pragma warning(disable : 4714)
-#include <boost/config/compiler/visualc.hpp>
-#else
-#include <boost/config/compiler/gcc.hpp>
-#endif
-#include <boost/predef.h>
-#include <boost/thread.hpp>
-#include <mutex>
-
-// #include <boost/thread/mutex.hpp>
-
-namespace HookLib
-{
-
-///
-/// trampolines are runtime-generated mini-functions that are used to call the
-/// original code of a function
-///
-class TrampolinePool
-{
-public:
- /// Call initialize before you use the TrampolinePool
- static void initialize();
-
- static TrampolinePool& instance()
- {
- // This is a very very sensitive place so we want to keep this function as simple as
- // possible and having it inlined probably cann't hurt
- return *s_Instance;
- }
-
- void setBlock(bool block);
-
- ///
- /// store a stub without moving code from the original function. This is used in cases
- /// where the hook can be placed without overwriting logic (i.e. hot-patchable
- /// functions and when chaining hooks)
- /// \param reroute the stub function to call before the regular function (on x86 this
- /// needs to be cdecl calling convention!)
- /// \param original the original function
- /// \param returnAddress address under which the original functionality can be
- /// reached.
- /// for the first hook this should be (original + 2), otherwise
- /// the address of the next hook in the chain
- /// \return address of the created trampoline function
- ///
- LPVOID storeStub(LPVOID reroute, LPVOID original, LPVOID returnAddress);
-
- ///
- /// store a stub, moving part of the original function to the trampoline
- /// \param reroute the stub function to call before the regular function (on x86 this
- /// needs to be cdecl calling convention!)
- /// \param original the original function
- /// \param preambleSize number of bytes from the original function to backup. Needs to
- /// correspond to complete instructions
- /// \param rerouteOffset offset in bytes from the created trampoline to the preamble
- /// that leads back to the original code
- /// \return address of the created trampoline function
- ///
- LPVOID storeStub(LPVOID reroute, LPVOID original, size_t preambleSize,
- size_t* rerouteOffset);
-
- ///
- /// store a trampoline for hot-patchable functions, where the original function
- /// is unharmed.
- /// \param reroute the reroute function
- /// \param original original function
- /// \param returnAddress address under which the original functionality can be
- /// reached.
- /// \return address of the trampoline function
- ///
- LPVOID storeTrampoline(LPVOID reroute, LPVOID original, LPVOID returnAddress);
-
- ///
- /// store a trampoline, copying a part of the original function to the trampoline.
- /// This is used for the case where the hooking mechanism needs to overwrite part of
- /// the function
- /// \param reroute the reroute function
- /// \param original original function
- /// \param preambleSize number of bytes from the original function to backup. Needs to
- /// correspond to complete instructions
- /// \param rerouteOffset offset in bytes from the created trampoline to the preamble
- /// that leads us back to the original code
- /// \return address of the trampoline function
- ///
- LPVOID storeTrampoline(LPVOID reroute, LPVOID original, size_t preambleSize,
- size_t* rerouteOffset);
-
- ///
- /// \param addressNear used to find a trampoline buffer near the jump instruction
- /// \return retrieve address of current trampoline buffer
- ///
- LPVOID currentBufferAddress(LPVOID addressNear);
-
- ///
- /// \brief forces the barrier(s) for the current thread to be released
- ///
- void forceUnlockBarrier();
-
-private:
- struct BufferList
- {
- size_t offset;
- std::vector<LPVOID> buffers;
- };
-
- typedef std::map<LPVOID, BufferList> BufferMap;
- static const intptr_t ADDRESS_MASK =
- 0xFFFFFFFFFF000000LL; // mask to "round" addresses to consolidate near
- // trampolines
-
-private:
- TrampolinePool();
-
- TrampolinePool& operator=(const TrampolinePool& reference); // not implemented
-
- /**
- * @brief allocates a buffer with read, write and execute rights near the
- * specified adress. The purpose is that we want to be able to jump from
- * adressNear to generated code with a 5-byte jump, even on x64 systems.
- * @param addressNear the reference adress
- * @note the resulting buffer is stored in the m_Buffers map
- */
- BufferMap::iterator allocateBuffer(LPVOID addressNear);
-
- void addBarrier(LPVOID rerouteAddr, LPVOID original, asmjit::X86Assembler& assembler);
-
-#if BOOST_ARCH_X86_64
- void copyCode(asmjit::X86Assembler& assembler, LPVOID source, size_t numBytes);
-#endif // BOOST_ARCH_X86_64
-
- BufferList& getBufferList(LPVOID address);
-
- LPVOID roundAddress(LPVOID address) const;
-
-public:
- static LPVOID __stdcall barrier(LPVOID function);
- static LPVOID __stdcall release(LPVOID function);
-
- LPVOID barrierInt(LPVOID function);
- LPVOID releaseInt(LPVOID function);
-
- void addCallToStub(asmjit::X86Assembler& assembler, LPVOID original, LPVOID reroute);
-
-private:
- ///
- /// \brief add a jump to an address outside the custom generated asm code (without
- /// modifying registers)
- /// \param assembler the assembler generator to write to
- /// \param destination destination address
- /// \note this currently generates a lot code on x64, may be overly complicated
- ///
- void addAbsoluteJump(asmjit::X86Assembler& assembler, uint64_t destination);
-
- DWORD determinePageSize();
-
-private:
-#if BOOST_ARCH_X86_64
- static const int SIZE_OF_JUMP = 13;
-#elif BOOST_ARCH_X86_32
- static const int SIZE_OF_JUMP = 5;
-#endif
-
- // the hook lib tries to allocate buffer close to the hooked functions,
- // for 32-bits application, this often falls on 0x40000000, which is the
- // address at which DLLs can be loaded on Windows
- //
- // some old-style DRM (e.g. Dragon Age 2) check that address and prevent
- // the game from running if it's not 0x40000000, so we give them a bit of
- // spaces before allocating our buffers
- //
- static constexpr uintptr_t MIN_ALLOC_ADDR = 0x40000000 + 0x200000;
-
- static TrampolinePool* s_Instance;
-
- bool m_FullBlock{false};
-
- BufferMap m_Buffers;
-
- typedef std::map<void*, void*> TThreadMap;
- boost::thread_specific_ptr<TThreadMap> m_ThreadGuards;
-
- LPVOID m_BarrierAddr;
- LPVOID m_ReleaseAddr;
-
- DWORD m_BufferSize = {1024};
- size_t m_SearchRange;
- uint64_t m_AddressMask;
-
- int m_MaxTrampolineSize;
-};
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/udis86wrapper.cpp b/libs/usvfs/src/thooklib/udis86wrapper.cpp
deleted file mode 100644
index 1ac7332..0000000
--- a/libs/usvfs/src/thooklib/udis86wrapper.cpp
+++ /dev/null
@@ -1,102 +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 "udis86wrapper.h"
-#include "pch.h"
-#include <boost/predef.h>
-#include <shmlogger.h>
-#include <stdexcept>
-
-namespace HookLib
-{
-
-UDis86Wrapper::UDis86Wrapper()
-{
- ud_init(&m_Obj);
- ud_set_syntax(&m_Obj, UD_SYN_INTEL);
-#if BOOST_ARCH_X86_64
- ud_set_mode(&m_Obj, 64);
-#else
- ud_set_mode(&m_Obj, 32);
-#endif
-}
-
-void UDis86Wrapper::setInputBuffer(const uint8_t* buffer, size_t size)
-{
- m_Buffer = buffer;
- ud_set_input_buffer(&m_Obj, buffer, size);
- ud_set_pc(&m_Obj, reinterpret_cast<uint64_t>(m_Buffer));
-}
-
-ud_t& UDis86Wrapper::obj()
-{
- return m_Obj;
-}
-
-bool UDis86Wrapper::isRelativeJump()
-{
- ud_mnemonic_code code = ud_insn_mnemonic(&m_Obj);
- // all conditional jumps and loops are relative, as are unconditional jumps with an
- // offset operand
- return (code == UD_Ijo) || (code == UD_Ijno) || (code == UD_Ijb) ||
- (code == UD_Ijae) || (code == UD_Ijz) || (code == UD_Ijnz) ||
- (code == UD_Ijbe) || (code == UD_Ija) || (code == UD_Ijs) ||
- (code == UD_Ijns) || (code == UD_Ijp) || (code == UD_Ijnp) ||
- (code == UD_Ijl) || (code == UD_Ijge) || (code == UD_Ijle) ||
- (code == UD_Ijg) || (code == UD_Ijcxz) || (code == UD_Ijecxz) ||
- (code == UD_Ijrcxz) || (code == UD_Iloop) || (code == UD_Iloope) ||
- (code == UD_Iloopne) ||
- ((code == UD_Icall) && (ud_insn_opr(&m_Obj, 0)->type == UD_OP_JIMM)) ||
- ((code == UD_Ijmp) && (ud_insn_opr(&m_Obj, 0)->type == UD_OP_JIMM));
-}
-
-intptr_t UDis86Wrapper::jumpOffset()
-{
- const ud_operand_t* op = ud_insn_opr(&m_Obj, 0);
- switch (op->size) {
- case 8:
- return static_cast<intptr_t>(op->lval.sbyte);
- case 16:
- return static_cast<intptr_t>(op->lval.sword);
- case 32:
- return static_cast<intptr_t>(op->lval.sdword);
- case 64:
- return static_cast<intptr_t>(op->lval.sqword);
- default:
- throw std::runtime_error("unsupported jump size");
- }
-}
-
-uint64_t UDis86Wrapper::jumpTarget()
-{
- // TODO: assert we're actually on a jump
-
- uint64_t res = ud_insn_off(&m_Obj) + ud_insn_len(&m_Obj);
-
- res += jumpOffset();
-
- if (ud_insn_opr(&m_Obj, 0)->base == UD_R_RIP) {
- res = *reinterpret_cast<uintptr_t*>(res);
- }
-
- return res;
-}
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/udis86wrapper.h b/libs/usvfs/src/thooklib/udis86wrapper.h
deleted file mode 100644
index 48d8a42..0000000
--- a/libs/usvfs/src/thooklib/udis86wrapper.h
+++ /dev/null
@@ -1,62 +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 <udis86.h>
-#undef inline // libudis86/types.h defines inline to __inline which is no longer legal
- // since vs2012
-
-namespace HookLib
-{
-
-class UDis86Wrapper
-{
-
-public:
- UDis86Wrapper();
-
- void setInputBuffer(const uint8_t* buffer, size_t size);
-
- ud_t& obj();
-
- operator ud_t*() { return &m_Obj; }
-
- bool isRelativeJump();
-
- intptr_t jumpOffset();
-
- ///
- /// determines the absolute jump target at the current instruction, taking into
- /// account relative instructions of all sizes and RIP-relative addressing.
- /// \return absolute address of the jump at the current disassembler instruction
- /// \note this works correctly ONLY if the input buffer has been set with
- /// setInputBuffer or
- /// if ud_set_pc has been called
- ///
- uint64_t jumpTarget();
-
-private:
-private:
- ud_t m_Obj;
- const uint8_t* m_Buffer{nullptr};
-};
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/utility.cpp b/libs/usvfs/src/thooklib/utility.cpp
deleted file mode 100644
index 124141f..0000000
--- a/libs/usvfs/src/thooklib/utility.cpp
+++ /dev/null
@@ -1,86 +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 "utility.h"
-#include "exceptionex.h"
-#include <cstdlib>
-
-namespace HookLib
-{
-
-FARPROC MyGetProcAddress(HMODULE module, LPCSTR functionName)
-{
- // determine position of the exports of the module
- PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)module;
- if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- return nullptr;
- }
-
- PIMAGE_NT_HEADERS ntHeaders =
- (PIMAGE_NT_HEADERS)(((LPBYTE)dosHeader) + dosHeader->e_lfanew);
- if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
- return nullptr;
- }
-
- PIMAGE_OPTIONAL_HEADER optionalHeader = &ntHeaders->OptionalHeader;
- if (optionalHeader->NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) {
- return nullptr;
- }
- PIMAGE_DATA_DIRECTORY dataDirectory =
- &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
- PIMAGE_EXPORT_DIRECTORY exportDirectory =
- (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)dosHeader + dataDirectory->VirtualAddress);
-
- ULONG* addressOfNames = (ULONG*)((LPBYTE)module + exportDirectory->AddressOfNames);
- ULONG* funcAddr = (ULONG*)((LPBYTE)module + exportDirectory->AddressOfFunctions);
-
- // search exports for the specified name
- for (DWORD i = 0; i < exportDirectory->NumberOfNames; ++i) {
- char* curFunctionName = (char*)((LPBYTE)module + addressOfNames[i]);
- USHORT* nameOrdinals =
- (USHORT*)((LPBYTE)module + exportDirectory->AddressOfNameOrdinals);
- if (strcmp(functionName, curFunctionName) == 0) {
- if (funcAddr[nameOrdinals[i]] >= dataDirectory->VirtualAddress &&
- funcAddr[nameOrdinals[i]] <
- dataDirectory->VirtualAddress + dataDirectory->Size) {
- char* forwardLibName = _strdup((LPSTR)module + funcAddr[nameOrdinals[i]]);
- ON_BLOCK_EXIT([forwardLibName]() {
- free(forwardLibName);
- });
- char* forwardFunctionName = strchr(forwardLibName, '.');
- *forwardFunctionName = 0;
- ++forwardFunctionName;
-
- HMODULE forwardLib = LoadLibraryA(forwardLibName);
- FARPROC forward = nullptr;
- if (forwardLib != nullptr) {
- forward = MyGetProcAddress(forwardLib, forwardFunctionName);
- FreeLibrary(forwardLib);
- }
-
- return forward;
- }
- return (FARPROC)((LPBYTE)module + funcAddr[nameOrdinals[i]]);
- }
- }
- return nullptr;
-}
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/thooklib/utility.h b/libs/usvfs/src/thooklib/utility.h
deleted file mode 100644
index e0be9f7..0000000
--- a/libs/usvfs/src/thooklib/utility.h
+++ /dev/null
@@ -1,35 +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 <windows_sane.h>
-
-namespace HookLib
-{
-
-/// \brief reimplementation of GetProcAddress to circumvent foreign hooks of
-/// GetProcAddress like AcLayer
-/// \param module handle to the module that contains the function or variable
-/// \param functionName function to retrieve the address of
-/// \return address of the exported function
-FARPROC MyGetProcAddress(HMODULE module, LPCSTR functionName);
-
-} // namespace HookLib
diff --git a/libs/usvfs/src/tinjectlib/CMakeLists.txt b/libs/usvfs/src/tinjectlib/CMakeLists.txt
deleted file mode 100644
index 096608e..0000000
--- a/libs/usvfs/src/tinjectlib/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(asmjit CONFIG REQUIRED)
-
-add_library(tinjectlib STATIC asmjit_sane.h injectlib.cpp injectlib.h)
-target_link_libraries(tinjectlib PRIVATE shared asmjit::asmjit)
-target_include_directories(tinjectlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-set_target_properties(tinjectlib PROPERTIES FOLDER injection)
diff --git a/libs/usvfs/src/tinjectlib/asmjit_sane.h b/libs/usvfs/src/tinjectlib/asmjit_sane.h
deleted file mode 100644
index 70ee117..0000000
--- a/libs/usvfs/src/tinjectlib/asmjit_sane.h
+++ /dev/null
@@ -1,32 +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
-
-#pragma warning(push, 3)
-#pragma warning(disable : 4201)
-#pragma warning(disable : 4244)
-#pragma warning(disable : 4245)
-// #ifndef ASMJIT_API
-// #define ASMJIT_API
-// #endif // ASMJIT_API
-#include <asmjit/asmjit.h>
-#include <asmjit/x86.h>
-#pragma warning(pop)
diff --git a/libs/usvfs/src/tinjectlib/injectlib.cpp b/libs/usvfs/src/tinjectlib/injectlib.cpp
deleted file mode 100644
index 2c7362a..0000000
--- a/libs/usvfs/src/tinjectlib/injectlib.cpp
+++ /dev/null
@@ -1,482 +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 <cstdio>
-#include <stdexcept>
-
-#include <boost/filesystem.hpp>
-#include <boost/predef.h>
-#include <boost/static_assert.hpp>
-#include <boost/type_traits.hpp>
-namespace fs = boost::filesystem;
-
-#include "injectlib.h"
-#include <addrtools.h>
-#include <exceptionex.h>
-#include <stringcast.h>
-#include <stringutils.h>
-// local version of asmjit with warning suppression
-#include "asmjit_sane.h"
-#include <TlHelp32.h>
-#include <spdlog/spdlog.h>
-
-using namespace asmjit;
-using namespace usvfs::shared;
-
-#if BOOST_ARCH_X86_64
-#pragma message("64bit build")
-using namespace x86;
-#elif BOOST_ARCH_X86_32
-#pragma message("32bit build")
-using namespace asmjit::x86;
-#else
-#error "unsupported architecture"
-#endif
-
-typedef HMODULE(WINAPI* TLoadLibraryType)(LPCWSTR);
-typedef FARPROC(WINAPI* TGetProcAddressType)(HMODULE, LPCSTR);
-typedef DWORD(WINAPI* TGetLastErrorType)();
-
-typedef BOOL(WINAPI* TSetXStateFeaturesMaskType)(PCONTEXT, DWORD64);
-
-static const size_t MAX_FUNCTIONAME = 20;
-
-struct TDataRemote
-{
- TLoadLibraryType loadLibrary;
- TGetProcAddressType getProcAddress;
- TGetLastErrorType getLastError;
- REGWORD returnAddress;
-
- char initFunction[MAX_FUNCTIONAME + 1];
- WCHAR dllName[MAX_PATH];
-};
-
-#if BOOST_ARCH_X86_64
-void pushAll(X86Assembler& assembler)
-{
- assembler.pushf();
- assembler.push(rax);
- assembler.push(rcx);
- assembler.push(rdx);
- assembler.push(rbx);
- assembler.push(rbp);
- assembler.push(rsi);
- assembler.push(rdi);
- assembler.push(r8);
- assembler.push(r9);
- assembler.push(r10);
- assembler.push(r11);
- assembler.push(r12);
- assembler.push(r13);
- assembler.push(r14);
- assembler.push(r15);
-}
-
-void popAll(X86Assembler& assembler)
-{
- assembler.pop(r15);
- assembler.pop(r14);
- assembler.pop(r13);
- assembler.pop(r12);
- assembler.pop(r11);
- assembler.pop(r10);
- assembler.pop(r9);
- assembler.pop(r8);
- assembler.pop(rdi);
- assembler.pop(rsi);
- assembler.pop(rbp);
- assembler.pop(rbx);
- assembler.pop(rdx);
- assembler.pop(rcx);
- assembler.pop(rax);
- assembler.popf();
-}
-#endif // BOOST_ARCH_X86_64
-
-void addStub(size_t userDataSize, X86Assembler& assembler, bool skipInit,
- TDataRemote* localData, TDataRemote* remoteData, LPCSTR initFunction)
-{
- Label Label_DLLLoaded = assembler.newLabel();
-
-#if BOOST_ARCH_X86_64
- pushAll(assembler);
- // call load library for the actual injection
- assembler.mov(rcx, imm(reinterpret_cast<int64_t>(&remoteData->dllName)));
- assembler.mov(rax, imm((intptr_t)(void*)localData->loadLibrary));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
-
- // cancel out of here if we failed to load the dll
- // TODO: would be great to report this error. But how?
- assembler.test(rax, rax);
- assembler.jnz(Label_DLLLoaded);
- /* this commented out code may seem pointless but it is a simple way to get at the
- error code when debugging. assembler.mov(rax,
- imm((intptr_t)(void*)localData->getLastError)); assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
- assembler.int3();*/
- popAll(assembler);
- assembler.ret();
- assembler.bind(Label_DLLLoaded);
-
- // determine address of the init function
- if (initFunction != nullptr) {
- Label Label_SkipInit = assembler.newLabel();
- assembler.mov(rcx, rax); // handle of the dll
- assembler.mov(rdx, imm(reinterpret_cast<int64_t>(
- remoteData->initFunction))); // name of init function
- assembler.mov(rax, imm((intptr_t)(void*)localData->getProcAddress));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
-
- if (skipInit) {
- assembler.test(rax, rax);
- assembler.jz(Label_SkipInit);
- }
-
- // call the init function with user data
- assembler.mov(rcx,
- imm(reinterpret_cast<int64_t>(remoteData) + sizeof(TDataRemote)));
- assembler.mov(rdx, imm(static_cast<int64_t>(userDataSize)));
- assembler.sub(rsp, 32);
- assembler.call(rax);
- assembler.add(rsp, 32);
- assembler.bind(Label_SkipInit);
- }
-
- // restore registers
- popAll(assembler);
-#else
-
- // save registers
- assembler.push(eax);
- assembler.pushf();
-
- // call load library for the actual injection
- assembler.push(imm(void_ptr_cast<int64_t>(remoteData->dllName)));
- // assembler.call(ptr_abs(static_cast<void*>(remoteData->loadLibrary)));
- assembler.mov(eax, imm(void_ptr_cast<int64_t>(localData->loadLibrary)));
- assembler.call(eax);
-
- assembler.test(eax, eax);
- assembler.jnz(Label_DLLLoaded);
- /* this commented out code may seem pointless but it is a simple way to get at the
- error code when debugging. assembler.mov(eax,
- imm((intptr_t)(void*)localData->getLastError)); assembler.call(eax);
- assembler.int3();*/
- assembler.popf();
- assembler.pop(eax);
- assembler.ret();
- assembler.bind(Label_DLLLoaded);
-
- // determine address of the init function
- if (initFunction != nullptr) {
- Label Label_SkipInit = assembler.newLabel();
- assembler.push(imm(
- void_ptr_cast<int64_t>(remoteData->initFunction))); // name of init function
- assembler.push(eax); // handle of the dll
- assembler.mov(eax, imm(void_ptr_cast<int64_t>(localData->getProcAddress)));
- assembler.call(eax);
- if (skipInit) {
- assembler.cmp(eax, 0);
- assembler.jz(Label_SkipInit);
- } else {
- assembler.cmp(eax, 0);
- assembler.jnz(Label_SkipInit);
- // heading for a crash! give an attached debugger a chance to analyse the error
- assembler.mov(eax, imm(void_ptr_cast<int64_t>(localData->getLastError)));
- assembler.call(eax);
- assembler.int3();
- assembler.bind(Label_SkipInit);
- }
-
- // call the init function with user data
- assembler.push(userDataSize);
- assembler.push(imm(void_ptr_cast<int64_t>(remoteData) + sizeof(TDataRemote)));
- assembler.call(eax);
- // init function is declared __cdecl so we have to remove parameters from the stack
- assembler.pop(eax);
- assembler.pop(eax);
- if (skipInit) {
- assembler.bind(Label_SkipInit);
- }
- }
-
- // restore registers
- assembler.popf();
- assembler.pop(eax);
-
-#endif
-}
-
-REGWORD WriteInjectionStub(HANDLE processHandle, LPCWSTR dllName, LPCSTR initFunction,
- LPCVOID userData, size_t userDataSize, bool skipInit,
- REGWORD returnAddress)
-{
- HMODULE k32mod = ::LoadLibrary(__TEXT("kernel32.dll"));
- TDataRemote data = {0};
-
- if (k32mod != nullptr) {
- data.loadLibrary =
- reinterpret_cast<TLoadLibraryType>(GetProcAddress(k32mod, "LoadLibraryW"));
- data.getProcAddress =
- reinterpret_cast<TGetProcAddressType>(GetProcAddress(k32mod, "GetProcAddress"));
- data.getLastError =
- reinterpret_cast<TGetLastErrorType>(GetProcAddress(k32mod, "GetLastError"));
- if ((data.loadLibrary == nullptr) || (data.getProcAddress == nullptr) ||
- (data.getLastError == nullptr)) {
- throw windows_error("failed to determine address for required functions");
- }
- } else {
- throw windows_error("kernel32.dll not loaded?");
- }
-
- data.returnAddress = returnAddress;
-
- if (initFunction != nullptr) {
- strncpy_s(data.initFunction, MAX_FUNCTIONAME, initFunction, MAX_FUNCTIONAME);
- data.initFunction[MAX_FUNCTIONAME] = '\0';
- } else {
- data.initFunction[0] = '\0';
- }
-
- wcsncpy_s(data.dllName, MAX_PATH, dllName, MAX_PATH - 1);
- data.dllName[MAX_PATH - 1] = L'\0';
-
- size_t totalSize = sizeof(TDataRemote) + userDataSize;
-
- // allocate memory in the target process and write the data-block there
- LPVOID remoteMem = VirtualAllocEx(processHandle, nullptr, totalSize,
- MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
-
- if (remoteMem == nullptr) {
- throw windows_error("failed to allocate memory in target process");
- }
-
- SIZE_T written;
- if (!WriteProcessMemory(processHandle, remoteMem, &data, sizeof(TDataRemote),
- &written)) {
- throw windows_error("failed to write control data to target process");
- }
- if (written != sizeof(TDataRemote)) {
- throw windows_error("failed to write whole control data to target process");
- }
-
- // write user data to remote memory if necessary
- if (userData != nullptr) {
- if (!WriteProcessMemory(processHandle, AddrAdd(remoteMem, sizeof(TDataRemote)),
- userData, userDataSize, &written)) {
- throw windows_error("failed to write user data to target process");
- }
- if (written != userDataSize) {
- throw windows_error("failed to write whole user data to target process");
- }
- }
-
- TDataRemote* remoteData = reinterpret_cast<TDataRemote*>(remoteMem);
-
- // now for the interesting part: write a stub into the target process that is run
- // before any code of the original binary.
-
- JitRuntime runtime;
-#if BOOST_ARCH_X86_64
- X86Assembler assembler(&runtime);
- if (returnAddress != 0) {
- // put return address on the stack
- // (this damages rax which hopefully doesn't matter)
- assembler.mov(rax, imm((intptr_t)(void*)data.returnAddress));
- assembler.push(rax);
- } // otherwise no return address was specified here. It better be on the stack
- // already
-#else
- X86Assembler assembler(&runtime);
- if (returnAddress != 0) {
- assembler.push(imm((intptr_t)(void*)data.returnAddress));
- }
-#endif
-
- addStub(userDataSize, assembler, skipInit, &data, remoteData, initFunction);
- assembler.ret(0);
-
- size_t stubSize = assembler.getCodeSize();
-
- // reserve memory for the stub
- PBYTE stubRemote = reinterpret_cast<PBYTE>(
- VirtualAllocEx(processHandle, nullptr, stubSize, MEM_COMMIT | MEM_RESERVE,
- PAGE_EXECUTE_READWRITE));
- if (stubRemote == nullptr) {
- throw windows_error("failed to allocate memory for stub");
- }
-
- // almost there. copy stub to target process
- if (!WriteProcessMemory(processHandle, stubRemote, assembler.getBuffer(), stubSize,
- &written) ||
- (written != stubSize)) {
- throw windows_error("failed to write stub to target process");
- }
-
- return reinterpret_cast<REGWORD>(stubRemote);
-}
-
-void InjectDLLEIP(HANDLE processHandle, HANDLE threadHandle, LPCWSTR dllName,
- LPCSTR initFunction, LPCVOID userData, size_t userDataSize,
- bool skipInit)
-{
- threadHandle =
- OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME),
- FALSE, GetThreadId(threadHandle));
-
- if (threadHandle == nullptr) {
- throw windows_error("failed to open thread");
- }
-
- CONTEXT threadContext;
- threadContext.ContextFlags = CONTEXT_CONTROL;
-
- // documentation says starting with Win7 SP1 you HAVE to call SetXStateFeaturesMask
- HMODULE k32mod = ::LoadLibrary(__TEXT("kernel32.dll"));
- if (k32mod == nullptr) {
- throw windows_error("failed to load kernel32.dll");
- }
- TSetXStateFeaturesMaskType sxsfm = reinterpret_cast<TSetXStateFeaturesMaskType>(
- GetProcAddress(k32mod, "SetXStateFeaturesMask"));
- if (sxsfm != nullptr) {
- sxsfm(&threadContext, 0);
- }
- ::FreeLibrary(k32mod);
-
- if (GetThreadContext(threadHandle, &threadContext) == 0) {
- throw windows_error("failed to access thread context.");
- }
-
-#if BOOST_ARCH_X86_64
- REGWORD returnAddress = threadContext.Rip;
-#else
- REGWORD returnAddress = threadContext.Eip;
-#endif
-
- REGWORD stubAddress =
- WriteInjectionStub(processHandle, dllName, initFunction, userData, userDataSize,
- skipInit, returnAddress);
-
- // make the stub the new next thing for the thread to execute
-#if BOOST_ARCH_X86_64
- threadContext.Rip = stubAddress;
-#else
- threadContext.Eip = stubAddress;
-#endif
-
- if (SetThreadContext(threadHandle, &threadContext) == 0) {
- throw windows_error("failed to overwrite thread context");
- }
-}
-
-void InjectDLLRemoteThread(HANDLE processHandle, LPCWSTR dllName, LPCSTR initFunction,
- LPCVOID userData, size_t userDataSize, bool skipInit)
-{
- REGWORD stubAddress = WriteInjectionStub(processHandle, dllName, initFunction,
- userData, userDataSize, skipInit, 0);
-
- DWORD threadId = 0UL;
- HANDLE threadHandle = CreateRemoteThread(
- processHandle, nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(stubAddress),
- nullptr, 0, &threadId);
- if (threadHandle == nullptr) {
- throw windows_error("failed to start remote thread");
- }
- ResumeThread(threadHandle);
- spdlog::get("usvfs")->info("waiting for {0:x} to complete",
- GetThreadId(threadHandle));
- ::WaitForSingleObject(threadHandle, 100);
- ::CloseHandle(threadHandle);
-}
-
-void InjectLib::InjectDLL(HANDLE processHandle, HANDLE threadHandle, LPCWSTR dllName,
- LPCSTR initFunction, LPCVOID userData, size_t userDataSize,
- bool skipInit)
-{
- namespace bfs = boost::filesystem;
- if (!exists(bfs::path(dllName))) {
- USVFS_THROW_EXCEPTION(file_not_found_error()
- << ex_msg(string_cast<std::string>(dllName)));
- }
- if (threadHandle == INVALID_HANDLE_VALUE) {
-#pragma message( \
- "doesn't seem to work as usvfs causes an exception in the first static initialization or pretty much on any function call. Because process is in different session? CRT related?")
- /*
- InjectDLLRemoteThread(processHandle, dllName,
- initFunction, userData, userDataSize, skipInit);
- */
-
- DWORD pid = GetProcessId(processHandle);
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
- THREADENTRY32 threadInfo;
- threadInfo.dwSize = sizeof(THREADENTRY32);
- BOOL moreThreads = Thread32First(snapshot, &threadInfo);
- std::vector<HANDLE> threadHandles;
- HANDLE injectThread = INVALID_HANDLE_VALUE;
- FILETIME injectThreadTime;
- spdlog::get("usvfs")->info("inject dll to process {0}", pid);
- while (moreThreads) {
- if (threadInfo.th32OwnerProcessID == pid) {
- HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, threadInfo.th32ThreadID);
-
- if (thread != nullptr) {
- DWORD suspCount = SuspendThread(thread);
- if (suspCount == 0) {
- FILETIME creationTime, exitTime, kernelTime, userTime;
- ::GetThreadTimes(thread, &creationTime, &exitTime, &kernelTime, &userTime);
-
- if ((injectThread == INVALID_HANDLE_VALUE) ||
- (CompareFileTime(&creationTime, &injectThreadTime) < 0)) {
- spdlog::get("usvfs")->info("candidate for oldest thread: {0}",
- threadInfo.th32ThreadID);
- injectThread = thread;
- injectThreadTime = creationTime;
- }
- }
- threadHandles.push_back(thread);
- }
- }
- moreThreads = Thread32Next(snapshot, &threadInfo);
- }
- if (injectThread != INVALID_HANDLE_VALUE) {
- spdlog::get("usvfs")->debug("going to inject dll");
- InjectDLLEIP(processHandle, injectThread, dllName, initFunction, userData,
- userDataSize, skipInit);
- } else {
- spdlog::get("usvfs")->critical("found no thread to use for injecting");
- }
-
- for (HANDLE hdl : threadHandles) {
- spdlog::get("usvfs")->info("resuming thread {0}", ::GetThreadId(hdl));
- ResumeThread(hdl);
- CloseHandle(hdl);
- }
- CloseHandle(snapshot);
- } else {
- InjectDLLEIP(processHandle, threadHandle, dllName, initFunction, userData,
- userDataSize, skipInit);
- }
-}
diff --git a/libs/usvfs/src/tinjectlib/injectlib.h b/libs/usvfs/src/tinjectlib/injectlib.h
deleted file mode 100644
index f165667..0000000
--- a/libs/usvfs/src/tinjectlib/injectlib.h
+++ /dev/null
@@ -1,50 +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 <windows_sane.h>
-
-namespace InjectLib
-{
-
-/**
- * @brief inject a dll into the target process
- * @param processHandle handle of the process to inject to
- * @param threadHandle handle of the main thread in the process
- * @param dllname name/path of the dll to inject. The path can't be longer than MAX_PATH
- * characters
- * @param initFunction name of the initialization function. Can't be longer than 20
- * characters. If this is null, no function is called. Important: the init function has
- * to exist, be exported, take the two userdata parameters and must be __cdecl calling
- * convention on 32bit windows! Failing any of these the target process will crash or
- * the dll isn't loaded Why __cdecl? Because otherwise we would need .def files to
- * export the init function with a GetProcAddress-able function name.
- * @param userData data passed to the init function
- * @param userDataSize size of the data to be passed
- * @param skipInit skip the call to the init function if the named function wasn't found
- * in the dll. If false, the target process will crash if the function isn't exported in
- * the dll
- */
-void InjectDLL(HANDLE processHandle, HANDLE threadHandle, LPCWSTR dllName,
- LPCSTR initFunction = nullptr, LPCVOID userData = nullptr,
- size_t userDataSize = 0, bool skipInit = false);
-
-} // namespace InjectLib
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
diff --git a/libs/usvfs/src/usvfs_helper/CMakeLists.txt b/libs/usvfs/src/usvfs_helper/CMakeLists.txt
deleted file mode 100644
index 5e083d9..0000000
--- a/libs/usvfs/src/usvfs_helper/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-add_library(usvfs_helper STATIC inject.h inject.cpp)
-target_link_libraries(usvfs_helper PUBLIC shared PRIVATE tinjectlib)
-target_include_directories(usvfs_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-set_target_properties(usvfs_helper PROPERTIES FOLDER injection)
diff --git a/libs/usvfs/src/usvfs_helper/inject.cpp b/libs/usvfs/src/usvfs_helper/inject.cpp
deleted file mode 100644
index bb1cb58..0000000
--- a/libs/usvfs/src/usvfs_helper/inject.cpp
+++ /dev/null
@@ -1,193 +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 <string>
-#include <utility>
-
-#include <spdlog/spdlog.h>
-
-#include <boost/filesystem.hpp>
-
-#include "inject.h"
-#include <exceptionex.h>
-#include <formatters.h>
-#include <injectlib.h>
-#include <loghelpers.h>
-#include <pch.h>
-#include <stringcast.h>
-#include <stringutils.h>
-#include <usvfsparametersprivate.h>
-#include <winapi.h>
-
-namespace ush = usvfs::shared;
-
-using namespace winapi;
-
-void usvfs::injectProcess(const std::wstring& applicationPath,
- const usvfsParameters& parameters,
- const PROCESS_INFORMATION& processInfo)
-{
- injectProcess(applicationPath, parameters, processInfo.hProcess, processInfo.hThread);
-}
-
-void usvfs::injectProcess(const std::wstring& applicationPath,
- const usvfsParameters& parameters, HANDLE processHandle,
- HANDLE threadHandle)
-{
- bool proc64 = false;
- bool sameBitness = false;
- {
- SYSTEM_INFO info;
- GetSystemInfo(&info);
- BOOL wow64;
- IsWow64Process(processHandle, &wow64);
- if (wow64) {
- // process is running under wow64 so it has to be a 32bit process running on 64bit
- // windows
- proc64 = false;
- BOOL temp;
- IsWow64Process(GetCurrentProcess(), &temp);
- sameBitness = temp == TRUE;
- } else {
- BOOL selfWow64;
- IsWow64Process(GetCurrentProcess(), &selfWow64);
- if (selfWow64) {
- // WE are a 32 bit process running on 64bit windows. the other process isn't, so
- // its 64bit
- proc64 = true;
- } else {
- sameBitness = true;
- // we have the same bitness as that other process, but which is it?
-#ifdef _WIN64
- proc64 = true;
-#else
- proc64 = false;
-#endif
- }
- }
- }
- boost::filesystem::path binPath = boost::filesystem::path(applicationPath);
- spdlog::get("usvfs")->info("injecting to process {} with {} bitness",
- ::GetProcessId(processHandle),
- sameBitness ? "same" : "different");
-
- if (sameBitness) {
- static constexpr auto USVFS_DLL =
-#ifdef _WIN64
- L"usvfs_x64.dll";
-#else
- L"usvfs_x86.dll";
-#endif
- const auto& preferedDll = binPath / USVFS_DLL;
- boost::filesystem::path dllPath = preferedDll;
- bool dllFound = boost::filesystem::exists(dllPath);
- // support for runing tests using a usvfs dll in lib folder (and proxy under bin):
- if (!dllFound && binPath.filename() == L"bin") {
- dllPath = binPath.parent_path() / L"lib" / USVFS_DLL;
- dllFound = boost::filesystem::exists(dllPath);
- }
- if (!dllFound) {
- USVFS_THROW_EXCEPTION(
- file_not_found_error()
- << ex_msg(std::string("dll missing: ") +
- ush::string_cast<std::string>(preferedDll.wstring()).c_str()));
- }
-
- spdlog::get("usvfs")->info("dll path: {}", dllPath.wstring());
-
- InjectLib::InjectDLL(processHandle, threadHandle, dllPath.c_str(), "InitHooks",
- &parameters, sizeof(parameters));
-
- spdlog::get("usvfs")->info("injection to same bitness process {} successful",
- ::GetProcessId(processHandle));
- } else {
- // first try platform specific proxy exe:
- static constexpr auto USVFS_PREFERED_EXE =
-#ifdef _WIN64
- L"usvfs_proxy_x86.exe";
-#else
- L"usvfs_proxy_x64.exe";
-#endif
- const auto& preferedExe = binPath / USVFS_PREFERED_EXE;
- boost::filesystem::path exePath = preferedExe;
- bool exeFound = boost::filesystem::exists(exePath);
- // support for runing tests using a usvfs dll in lib folder (and proxy under bin):
- if (!exeFound && binPath.filename() == L"lib") {
- exePath = binPath.parent_path() / L"bin" / USVFS_PREFERED_EXE;
- exeFound = boost::filesystem::exists(exePath);
- }
- // finally fallback to old proxy naming (but only for 64bit as we don't have a 64bit
- // proxy in this case):
-#ifdef _WIN64
- if (!exeFound) {
- exePath = binPath / L"usvfs_proxy.exe";
- exeFound = boost::filesystem::exists(exePath);
- }
-#endif
- if (!exeFound) {
- USVFS_THROW_EXCEPTION(file_not_found_error() << ex_msg(
- std::string("usvfs proxy not found: ") +
- ush::string_cast<std::string>(preferedExe.wstring())));
- } else
- spdlog::get("usvfs")->info("using usvfs proxy: {}",
- ush::string_cast<std::string>(preferedExe.wstring()));
- // need to use proxy aplication to inject
- auto proxyProcess =
- std::move(wide::createProcess(exePath.wstring())
- .arg(L"--instance")
- .arg(ush::string_cast<std::wstring>(parameters.instanceName))
- .arg(L"--pid")
- .arg(GetProcessId(processHandle)));
-
- if (threadHandle != INVALID_HANDLE_VALUE) {
- proxyProcess.arg("--tid").arg(GetThreadId(threadHandle));
- }
- process::Result result = proxyProcess();
- if (!result.valid) {
- USVFS_THROW_EXCEPTION(unknown_error()
- << ex_msg(std::string("failed to start proxy ") +
- ush::string_cast<std::string>(exePath.wstring()))
- << ex_win_errcode(result.errorCode));
- } else {
- // wait for proxy completion. this shouldn't take long, 15 seconds is very
- // generous
- switch (WaitForSingleObject(result.processInfo.hProcess, 15000)) {
- case WAIT_TIMEOUT: {
- spdlog::get("usvfs")->debug("proxy timeout");
- TerminateProcess(result.processInfo.hProcess, 1);
- USVFS_THROW_EXCEPTION(timeout_error()
- << ex_msg(std::string("proxy didn't complete in time")));
- } break;
- case WAIT_FAILED: {
- spdlog::get("usvfs")->debug("proxy wait failed");
- TerminateProcess(result.processInfo.hProcess, 1);
- USVFS_THROW_EXCEPTION(unknown_error()
- << ex_msg(
- std::string("failed to wait for proxy completion"))
- << ex_win_errcode(result.errorCode));
- } break;
- default: {
- spdlog::get("usvfs")->debug("proxy run successful");
- // nop
- } break;
- }
- }
- }
-}
diff --git a/libs/usvfs/src/usvfs_helper/inject.h b/libs/usvfs/src/usvfs_helper/inject.h
deleted file mode 100644
index 10f179f..0000000
--- a/libs/usvfs/src/usvfs_helper/inject.h
+++ /dev/null
@@ -1,51 +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 "usvfsparameters.h"
-#include <string>
-#include <windows_sane.h>
-
-namespace usvfs
-{
-
-/**
- * @brief inject usvfs to a process
- * @param applicationPath
- * @param parameters
- * @param processInfo
- */
-void injectProcess(const std::wstring& applicationPath,
- const usvfsParameters& parameters,
- const PROCESS_INFORMATION& processInfo);
-
-/**
- * @brief inject usvfs to a process
- * @param applicationPath path to usvfs
- * @param parameters
- * @param process process handle to inject to
- * @param thread main thread inside that process. This can be set to
- * INVALID_HANDLE_VALUE in which case a new thread is created in the process
- */
-void injectProcess(const std::wstring& applicationPath,
- const usvfsParameters& parameters, HANDLE process, HANDLE thread);
-
-} // namespace usvfs
diff --git a/libs/usvfs/src/usvfs_proxy/CMakeLists.txt b/libs/usvfs/src/usvfs_proxy/CMakeLists.txt
deleted file mode 100644
index 47614ee..0000000
--- a/libs/usvfs/src/usvfs_proxy/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-find_package(Boost CONFIG REQUIRED COMPONENTS filesystem)
-
-add_executable(usvfs_proxy main.cpp version.rc)
-target_link_libraries(usvfs_proxy PRIVATE usvfs_dll shared usvfs_helper)
-set_target_properties(usvfs_proxy
- PROPERTIES
- RUNTIME_OUTPUT_NAME usvfs_proxy${ARCH_POSTFIX}
- RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_BINDIR}
- RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_BINDIR}
-)
-
-install(TARGETS usvfs_proxy EXPORT usvfs${ARCH_POSTFIX}Targets)
-install(FILES $<TARGET_PDB_FILE:usvfs_proxy> DESTINATION pdb OPTIONAL)
diff --git a/libs/usvfs/src/usvfs_proxy/main.cpp b/libs/usvfs/src/usvfs_proxy/main.cpp
deleted file mode 100644
index 88d4696..0000000
--- a/libs/usvfs/src/usvfs_proxy/main.cpp
+++ /dev/null
@@ -1,221 +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 "pch.h"
-#include <../usvfs_dll/hookcontext.h>
-#include <Psapi.h>
-#include <WinUser.h>
-#include <boost/algorithm/string/predicate.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <inject.h>
-#include <shared_memory.h>
-#include <sharedparameters.h>
-#include <shmlogger.h>
-#include <spdlog/spdlog.h>
-#include <usvfsparameters.h>
-#include <winapi.h>
-
-namespace bi = boost::interprocess;
-namespace bfs = boost::filesystem;
-using usvfs::SharedParameters;
-using usvfs::shared::SharedMemoryT;
-
-template <typename T>
-T getParameter(std::vector<std::string>& arguments, const std::string& key,
- bool consume)
-{
- auto iter = std::find(arguments.begin(), arguments.end(), std::string("--") + key);
- if ((iter != arguments.end()) && ((iter + 1) != arguments.end())) {
- T result = boost::lexical_cast<T>(*(iter + 1));
- if (consume) {
- arguments.erase(iter, iter + 2);
- }
- return result;
- } else {
- throw std::runtime_error(std::string("argument missing " + key));
- }
-}
-
-template <typename T>
-T getParameter(std::vector<std::string>& arguments, const std::string& key,
- const T& def, bool consume)
-{
- auto iter = std::find(arguments.begin(), arguments.end(), std::string("--") + key);
- if ((iter != arguments.end()) && ((iter + 1) != arguments.end())) {
- T result = boost::lexical_cast<T>(*(iter + 1));
- if (consume) {
- arguments.erase(iter, iter + 2);
- }
- return result;
- } else {
- return def;
- }
-}
-
-static void exceptionDialog(int line, int num, ...)
-{
- va_list args;
- va_start(args, num);
-
- std::wstring wstr;
- WCHAR buf[256];
- wstr.append(L"Unhandled USVFS proxy exception (line ");
- wsprintf(buf, L"%d): ", line);
- wstr.append(buf);
- for (int i = 0; i < num; i++) {
- wsprintf(buf, L"%S", va_arg(args, const char*));
- if (i < num - 1)
- wsprintf(buf, L", ");
- wstr.append(buf);
- }
-
- MessageBox(NULL, wstr.data(), NULL, MB_OK);
-
- va_end(args);
-}
-
-int main(int argc, char** argv)
-{
- std::shared_ptr<spdlog::logger> logger;
-
- std::vector<std::string> arguments;
- std::copy(argv + 1, argv + argc, std::back_inserter(arguments));
-
- std::string instance;
- try {
- SHMLogger::open("usvfs");
- logger = spdlog::create<usvfs::sinks::shm_sink>("usvfs", "usvfs");
- logger->set_pattern("%H:%M:%S.%e [%L] (proxy) %v");
-
- instance = getParameter<std::string>(arguments, "instance", true);
- } catch (const std::exception& e) {
- if (logger.get() == nullptr) {
- exceptionDialog(__LINE__, 1, e.what());
- return 1;
- }
- try {
- logger->critical("{}", e.what());
- } catch (const spdlog::spdlog_ex& e2) {
- exceptionDialog(__LINE__, 2, e.what(), e2.what());
- // no way to log this
- } catch (const std::exception&) {
- logger->critical(e.what());
- }
- return 1;
- }
-
- try {
- std::string executable =
- getParameter<std::string>(arguments, "executable", "", true);
- int pid = getParameter<int>(arguments, "pid", 0, true);
- int tid = getParameter<int>(arguments, "tid", 0, true);
-
- logger->info("instance: {}", instance);
- logger->info("exe: {}", executable);
- logger->info("pid: {}", pid);
-
- if (executable.empty() && (pid == 0)) {
- logger->warn("not all required settings set");
- return 1;
- }
-
- SharedMemoryT configurationSHM(bi::open_only, instance.c_str());
- if (!configurationSHM.check_sanity()) {
- logger->warn("failed to connect to vfs");
- return 1;
- }
- logger->info("size: {}", configurationSHM.get_size());
- logger->info("addr: {0:p}", configurationSHM.get_address());
- logger->info("objs: {}", configurationSHM.get_num_named_objects());
- std::pair<SharedParameters*, SharedMemoryT::size_type> params =
- configurationSHM.find<SharedParameters>("parameters");
- if (params.first == nullptr) {
- logger->error("failed to open shared configuration for {}", instance);
- return 1;
- }
-
- boost::filesystem::path p(winapi::wide::getModuleFileName(nullptr));
-
- if (executable.empty()) {
- HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- HANDLE threadHandle = INVALID_HANDLE_VALUE;
- if (tid != 0) {
- threadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
- }
-
- BOOL blacklisted = FALSE;
- TCHAR szModName[MAX_PATH];
-
- if (GetModuleFileNameEx(processHandle, NULL, szModName,
- sizeof(szModName) / sizeof(TCHAR))) {
- const auto appName = usvfs::shared::string_cast<std::string>(szModName);
-
- if (params.first->executableBlacklisted(appName, {})) {
- logger->info("not injecting {} as application is blacklisted", appName);
- blacklisted = TRUE;
- }
- }
-
- if (!blacklisted) {
- usvfs::injectProcess(p.parent_path().wstring(), params.first->makeLocal(),
- processHandle, threadHandle);
- }
- } else {
- winapi::process::Result process =
- winapi::ansi::createProcess(executable)
- .arguments(arguments.begin(), arguments.end())
- .workingDirectory(bfs::path(executable).parent_path().string())
- .suspended()();
-
- if (!process.valid) {
- return 1;
- }
-
- BOOL blacklisted = FALSE;
-
- if (params.first->executableBlacklisted(executable, {})) {
- logger->info("not injecting {} as application is blacklisted", executable);
-
- blacklisted = TRUE;
- }
-
- if (!blacklisted) {
- usvfs::injectProcess(p.parent_path().wstring(), params.first->makeLocal(),
- process.processInfo);
- }
-
- ResumeThread(process.processInfo.hThread);
- }
-
- return 0;
- } catch (const std::exception& e) {
- try {
- logger->critical("unhandled exception: {}", e.what());
- logExtInfo(e);
- } catch (const spdlog::spdlog_ex& e2) {
- // no way to log this
- exceptionDialog(__LINE__, 2, e.what(), e2.what());
- } catch (const std::exception&) {
- logger->critical(e.what());
- }
- }
-}
diff --git a/libs/usvfs/src/usvfs_proxy/version.rc b/libs/usvfs/src/usvfs_proxy/version.rc
deleted file mode 100644
index 03652c2..0000000
--- a/libs/usvfs/src/usvfs_proxy/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 Proxy\0"
-#ifdef _WIN64
- VALUE "OriginalFilename", "usvfs_proxy_x64.exe\0"
-#else
- VALUE "OriginalFilename", "usvfs_proxy_x86.exe\0"
-#endif
- VALUE "ProductName", "USVFS\0"
- VALUE "ProductVersion", VER_PRODUCTVERSION_STR
- END
- END
-
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0409L, 1200
- END
-END