diff options
Diffstat (limited to 'libs/usvfs/src/usvfs_dll/maptracker.h')
| -rw-r--r-- | libs/usvfs/src/usvfs_dll/maptracker.h | 691 |
1 files changed, 0 insertions, 691 deletions
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 |
