aboutsummaryrefslogtreecommitdiff
path: root/libs/usvfs/src/usvfs_dll/usvfs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/usvfs/src/usvfs_dll/usvfs.cpp')
-rw-r--r--libs/usvfs/src/usvfs_dll/usvfs.cpp965
1 files changed, 0 insertions, 965 deletions
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;
-}