diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 02:40:16 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 02:40:16 -0500 |
| commit | bf7a57fc55493247a624ecfd65e4d73964e7d8d2 (patch) | |
| tree | 0a4b2d6123672768187a5ab863e63cd3f21bb319 /libs | |
| parent | e0fb66428a9b78b4c326df8d7e30429a5b271d74 (diff) | |
Remove vendored usvfs and Windows-only usvfs code paths
The Linux port uses its own in-process FUSE VFS (FuseConnector), so
usvfs was dead weight: libs/usvfs/ was never built, usvfsconnector.cpp
was explicitly REMOVE_ITEM'd from the source list, and every call site
was already guarded by #ifdef _WIN32 with a working Linux branch (the
Windows build is also broken on this branch, since FUSE3 is a hard
find_package dependency).
- Drop libs/usvfs/ and src/src/usvfsconnector.{cpp,h}
- Collapse #ifdef _WIN32 / usvfs include branches in organizercore,
mainwindow, settings, util, spawn
- Remove getUsvfsVersionString() and the "usvfs: ..." log field
- Remove vfs32/64DLLName APPPARAMs (nothing reads them)
- Relabel About dialog "usvfs:" row to "VFS: FUSE 3"
Diffstat (limited to 'libs')
270 files changed, 0 insertions, 24441 deletions
diff --git a/libs/usvfs/.clang-format b/libs/usvfs/.clang-format deleted file mode 100644 index 6098e1f..0000000 --- a/libs/usvfs/.clang-format +++ /dev/null @@ -1,41 +0,0 @@ ---- -# We'll use defaults from the LLVM style, but with 4 columns indentation. -BasedOnStyle: LLVM -IndentWidth: 2 ---- -Language: Cpp -DeriveLineEnding: false -UseCRLF: true -DerivePointerAlignment: false -PointerAlignment: Left -AlignConsecutiveAssignments: true -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: Empty -AlwaysBreakTemplateDeclarations: Yes -AccessModifierOffset: -2 -AlignTrailingComments: true -SpacesBeforeTrailingComments: 2 -NamespaceIndentation: Inner -MaxEmptyLinesToKeep: 1 -BreakBeforeBraces: Custom -BraceWrapping: - AfterCaseLabel: false - AfterClass: true - AfterControlStatement: false - AfterEnum: true - AfterFunction: true - AfterNamespace: true - AfterStruct: true - AfterUnion: true - AfterExternBlock: true - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: true -ColumnLimit: 88 -ForEachMacros: ['Q_FOREACH', 'foreach'] diff --git a/libs/usvfs/.git-blame-ignore-revs b/libs/usvfs/.git-blame-ignore-revs deleted file mode 100644 index f5b2c23..0000000 --- a/libs/usvfs/.git-blame-ignore-revs +++ /dev/null @@ -1 +0,0 @@ -1c95b7452585e53ef97954248aed42480d5bb5de diff --git a/libs/usvfs/.gitattributes b/libs/usvfs/.gitattributes deleted file mode 100644 index f869712..0000000 --- a/libs/usvfs/.gitattributes +++ /dev/null @@ -1,7 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto - -# Explicitly declare text files you want to always be normalized and converted -# to native line endings on checkout. -*.cpp text eol=crlf -*.h text eol=crlf diff --git a/libs/usvfs/.github/workflows/build.yml b/libs/usvfs/.github/workflows/build.yml deleted file mode 100644 index 2acd535..0000000 --- a/libs/usvfs/.github/workflows/build.yml +++ /dev/null @@ -1,173 +0,0 @@ -name: Build USVFS - -on: - push: - branches: [master, dev/cmake] - tags: - - "*" - pull_request: - types: [opened, synchronize, reopened] - -env: - VCPKG_BINARY_SOURCES: clear;x-azblob,${{ vars.AZ_BLOB_VCPKG_URL }},${{ secrets.AZ_BLOB_SAS }},readwrite - -jobs: - build: - name: Build USVFS - strategy: - matrix: - arch: [x86, x64] - config: [Debug, Release] - runs-on: windows-2022 - steps: - # set VCPKG Root - - name: "Set environmental variables" - shell: bash - run: | - echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV - - # checkout USVFS and vcpkg - - uses: actions/checkout@v4 - - # configure - - run: cmake --preset vs2022-windows-${{ matrix.arch }} -B build_${{ matrix.arch }} "-DCMAKE_INSTALL_PREFIX=install/${{ matrix.config }}" - - # build - - run: cmake --build build_${{ matrix.arch }} --config ${{ matrix.config }} --target INSTALL - - # package install - - uses: actions/upload-artifact@master - with: - name: usvfs_${{ matrix.config }}_${{ matrix.arch }} - path: ./install/${{ matrix.config }} - - # package test/dlls/etc. for tests - - - uses: actions/upload-artifact@master - with: - name: usvfs-libs_${{ matrix.config }}_${{ matrix.arch }} - path: ./lib - - uses: actions/upload-artifact@master - with: - name: usvfs-bins_${{ matrix.config }}_${{ matrix.arch }} - path: ./bin - - uses: actions/upload-artifact@master - with: - name: usvfs-tests_${{ matrix.config }}_${{ matrix.arch }} - path: ./test/bin - - # merge x86 / x64 artifacts for tests (root bin/lib and test folder) - merge-artifacts-for-tests: - runs-on: ubuntu-latest - name: Merge Test Artifacts - needs: build - strategy: - matrix: - config: [Debug, Release] - steps: - - name: Merge USVFS libs - uses: actions/upload-artifact/merge@v4 - with: - name: usvfs-libs_${{ matrix.config }} - pattern: usvfs-libs_${{ matrix.config }}_* - - name: Merge USVFS bins - uses: actions/upload-artifact/merge@v4 - with: - name: usvfs-bins_${{ matrix.config }} - pattern: usvfs-bins_${{ matrix.config }}_* - - name: Merge USVFS tests - uses: actions/upload-artifact/merge@v4 - with: - name: usvfs-tests_${{ matrix.config }} - pattern: usvfs-tests_${{ matrix.config }}_* - - # merge x86 / x64 artifacts (install folder) - merge-artifacts-for-release: - runs-on: ubuntu-latest - name: Merge Install Artifacts - needs: build - strategy: - matrix: - config: [Debug, Release] - steps: - - name: Merge USVFS install - uses: actions/upload-artifact/merge@v4 - with: - name: usvfs_${{ matrix.config }} - pattern: usvfs_${{ matrix.config }}_* - - test: - name: Test USVFS - needs: merge-artifacts-for-tests - runs-on: windows-2022 - strategy: - matrix: - config: [Debug, Release] - arch: [x86, x64] - steps: - - uses: actions/checkout@v4 - - uses: actions/download-artifact@master - with: - name: usvfs-libs_${{ matrix.config }} - path: ./lib - - uses: actions/download-artifact@master - with: - name: usvfs-bins_${{ matrix.config }} - path: ./bin - - uses: actions/download-artifact@master - with: - name: usvfs-tests_${{ matrix.config }} - path: ./test/bin - - run: ./test/bin/shared_test_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/testinject_bin_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/thooklib_test_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/tinjectlib_test_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/tvfs_test_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/usvfs_test_runner_${{ matrix.arch }}.exe - if: always() - - run: ./test/bin/usvfs_global_test_runner_${{ matrix.arch }}.exe - if: always() - - uses: actions/upload-artifact@v4 - if: always() - with: - name: tests-outputs_${{ matrix.config }}_${{ matrix.arch }} - path: ./test/temp - if-no-files-found: ignore - retention-days: 7 - overwrite: true - - publish: - if: github.ref_type == 'tag' - needs: [merge-artifacts-for-release, test] - runs-on: windows-2022 - permissions: - contents: write - steps: - # USVFS does not use different names for debug and release so we are going to - # retrieve both install artifacts and put them under install/ and install/debug/ - - - name: Download Release Artifact - uses: actions/download-artifact@master - with: - name: usvfs_Release - path: ./install - - - name: Download Debug Artifact - uses: actions/download-artifact@master - with: - name: usvfs_Debug - path: ./install/debug - - - name: Create USVFS Base archive - run: 7z a usvfs_${{ github.ref_name }}.7z ./install/* - - - name: Publish Release - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - run: gh release create --draft=false --notes="Release ${{ github.ref_name }}" "${{ github.ref_name }}" ./usvfs_${{ github.ref_name }}.7z diff --git a/libs/usvfs/.github/workflows/linting.yml b/libs/usvfs/.github/workflows/linting.yml deleted file mode 100644 index d5e3f4b..0000000 --- a/libs/usvfs/.github/workflows/linting.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Lint USVFS - -on: - push: - pull_request: - types: [opened, synchronize, reopened] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Check format - uses: ModOrganizer2/check-formatting-action@master - with: - check-path: "." - exclude-regex: "third-party" diff --git a/libs/usvfs/.gitignore b/libs/usvfs/.gitignore deleted file mode 100644 index 1c1dec2..0000000 --- a/libs/usvfs/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# build outputs: -/bin -/lib -/test/bin -/build* -/install - -# local overides -/vsbuild/external_dependencies_local.props - -# build intermediates: -/vsbuild/Release -/vsbuild/ReleaseTest -/vsbuild/Debug -/vsbuild/DebugTest -/vsbuild32 -/vsbuild64 -CMakeUserPresets.json - -# test "side effects" -/test/temp - -# VS generated files: -.vs -*.aps -*.vcxproj.user -msbuild.log - -/stderr.log -/stderr_32.log -/stdout.log -/stdout_32.log - -.vscode diff --git a/libs/usvfs/.pre-commit-config.yaml b/libs/usvfs/.pre-commit-config.yaml deleted file mode 100644 index 3103a1f..0000000 --- a/libs/usvfs/.pre-commit-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-merge-conflict - - id: check-case-conflict - - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v19.1.5 - hooks: - - id: clang-format - 'types_or': [c++, c] - -ci: - autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks." - autofix_prs: true - autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate." - autoupdate_schedule: quarterly - submodules: false diff --git a/libs/usvfs/CMakeLists.txt b/libs/usvfs/CMakeLists.txt deleted file mode 100644 index e0deb42..0000000 --- a/libs/usvfs/CMakeLists.txt +++ /dev/null @@ -1,75 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -include(CMakePackageConfigHelpers) - -project(usvfs) - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -set(CMAKE_CXX_STANDARD 20) - -set(USVFS_BINDIR ${CMAKE_CURRENT_LIST_DIR}/bin) -set(USVFS_LIBDIR ${CMAKE_CURRENT_LIST_DIR}/lib) - -if (MSVC) - # /Zi generate PDBs - # /Gy enable function-level linking - # /Oi enable intrinsic function - add_compile_options("$<$<NOT:$<CONFIG:DEBUG>>:/Zi;/Gy;/Oi>") - - # /OPT:ICF enable COMDAT folding - # /DEBUG:FULL generate debug info (PDB) - # /OPT:REF enable references (PDB) - add_link_options("$<$<NOT:$<CONFIG:DEBUG>>:/OPT:ICF;/DEBUG:FULL;/OPT:REF>") -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(ARCH_POSTFIX _x86) -else() - set(ARCH_POSTFIX _x64) -endif() - -add_subdirectory(src/shared) - -add_subdirectory(src/thooklib) -add_subdirectory(src/tinjectlib) -add_subdirectory(src/usvfs_helper) - -add_subdirectory(src/usvfs_dll) -add_subdirectory(src/usvfs_proxy) - -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/usvfsConfig.cmake" - INSTALL_DESTINATION "lib/cmake/usvfs" - NO_SET_AND_CHECK_MACRO - NO_CHECK_REQUIRED_COMPONENTS_MACRO -) - -file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/usvfs/usvfs_version.h" usvfs_version) -string(REGEX MATCH "USVFS_VERSION_MAJOR ([0-9]*)" _ ${usvfs_version}) -set(usvfs_version_major ${CMAKE_MATCH_1}) -string(REGEX MATCH "USVFS_VERSION_MINOR ([0-9]*)" _ ${usvfs_version}) -set(usvfs_version_minor ${CMAKE_MATCH_1}) -string(REGEX MATCH "USVFS_VERSION_BUILD ([0-9]*)" _ ${usvfs_version}) -set(usvfs_version_build ${CMAKE_MATCH_1}) -string(REGEX MATCH "USVFS_VERSION_REVISION ([0-9]*)" _ ${usvfs_version}) -set(usvfs_version_revision ${CMAKE_MATCH_1}) - -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/usvfsConfigVersion.cmake" - VERSION "${usvfs_version_major}.${usvfs_version_minor}.${usvfs_version_build}.${usvfs_version_revision}" - COMPATIBILITY AnyNewerVersion - ARCH_INDEPENDENT -) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/usvfsConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/usvfsConfigVersion.cmake - DESTINATION lib/cmake/usvfs -) - -if (BUILD_TESTING) - enable_testing() - set(USVFS_TEST_BINDIR ${CMAKE_CURRENT_LIST_DIR}/test/bin) - add_subdirectory(test) -endif() diff --git a/libs/usvfs/CMakePresets.json b/libs/usvfs/CMakePresets.json deleted file mode 100644 index 156ccfe..0000000 --- a/libs/usvfs/CMakePresets.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "configurePresets": [ - { - "cacheVariables": { - "BUILD_TESTING": { - "type": "BOOL", - "value": "ON" - } - }, - "errors": { - "deprecated": true - }, - "hidden": true, - "name": "cmake-dev", - "warnings": { - "deprecated": true, - "dev": true - } - }, - { - "cacheVariables": { - "VCPKG_MANIFEST_NO_DEFAULT_FEATURES": { - "type": "BOOL", - "value": "ON" - } - }, - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", - "hidden": true, - "name": "vcpkg" - }, - { - "cacheVariables": { - "VCPKG_MANIFEST_FEATURES": { - "type": "STRING", - "value": "testing" - } - }, - "hidden": true, - "inherits": ["vcpkg"], - "name": "vcpkg-dev" - }, - { - "binaryDir": "${sourceDir}/vsbuild64", - "architecture": { - "strategy": "set", - "value": "x64" - }, - "cacheVariables": { - "VCPKG_TARGET_TRIPLET": { - "type": "STRING", - "value": "x64-windows-static-md" - } - }, - "hidden": true, - "name": "windows-x64" - }, - { - "binaryDir": "${sourceDir}/vsbuild32", - "architecture": { - "strategy": "set", - "value": "Win32" - }, - "cacheVariables": { - "VCPKG_TARGET_TRIPLET": { - "type": "STRING", - "value": "x86-windows-static-md" - } - }, - "hidden": true, - "name": "windows-x86" - }, - { - "cacheVariables": { - "CMAKE_CXX_FLAGS": "/EHsc /MP /W4" - }, - "generator": "Visual Studio 17 2022", - "inherits": ["cmake-dev", "vcpkg-dev"], - "hidden": true, - "name": "vs2022-windows", - "toolset": "v143" - }, - { - "inherits": ["vs2022-windows", "windows-x64"], - "name": "vs2022-windows-x64" - }, - { - "inherits": ["vs2022-windows", "windows-x86"], - "name": "vs2022-windows-x86" - } - ], - "buildPresets": [ - { - "name": "vs2022-windows-x64", - "configurePreset": "vs2022-windows-x64" - }, - { - "name": "vs2022-windows-x86", - "configurePreset": "vs2022-windows-x86" - } - ], - "version": 3 -} diff --git a/libs/usvfs/LICENSE b/libs/usvfs/LICENSE deleted file mode 100644 index c0000a0..0000000 --- a/libs/usvfs/LICENSE +++ /dev/null @@ -1,699 +0,0 @@ -Copyright (C) 2015-2024 Sebastian Herbord, ModOrganizer2 Team - -This program 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. - -This program 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. - -Please find a copy of the of the GNU General Public License at the end of this file or see <http://www.gnu.org/licenses>. - -Additional permissions are granted under GNU GPL version 3 section 7 to Free and Open Source Software ("FOSS") without requiring that such software is covered by the GPLv3. - - 1. Permission to link with the usvfs_x64.dll and/or usvfs_x86.dll - - 2. Permission to distribute unmodified binary copies of the usvfs library - -Theses permissions (and no other) are granted provided that the software: - - 1. Is distributed under a license that satisfies the Free Software Definition (https://www.gnu.org/philosophy/free-sw.en.html) or the Open Source Definition Version (https://opensource.org/osd) - - 2. Includes the copyright notice "usvfs - User-Space Virtual File System, Copyright (C) Sebastian Herbord", a copy of this license and a link to the usvfs repository in its user-interface and any user-facing documentation. - - 3. Is not linked or distributed with proprietary (non-FOSS) software. - - -------------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program 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. - - This program 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 this program. If not, see <http://www.gnu.org/licenses/>. - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -<http://www.gnu.org/licenses/>. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/libs/usvfs/README.md b/libs/usvfs/README.md deleted file mode 100644 index 1167810..0000000 --- a/libs/usvfs/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# USVFS - -[](http://www.gnu.org/licenses/gpl-3.0.en.html) -[](https://github.com/ModOrganizer2/usvfs/actions) - -USVFS (short for User Space Virtual File System) aims to allow windows applications to -create file or directory links that are visible to only a select set of processes. -It does so by using api hooking to fool file access functions into discovering/opening -files that are in fact somewhere else. - -## Current state - -USVFS is work in progress and should be considered in alpha state. -It is a core component of Mod Organizer v2 <https://github.com/ModOrganizer2/modorganizer> -and thus receives serious real world testing. - -## Building - -You will need `cmake`, Python 3+ and `vcpkg` to build USVFS: - -```pwsh -cmake --preset vs2022-windows-x64 -cmake --build --preset vs2022-windows-x64 --config Release - -# only if you need to hook x86 applications -cmake --preset vs2022-windows-x86 -cmake --build --preset vs2022-windows-x86 --config Release -``` - -## Comparison to symbolic links - -The following is based on the final goal for USVFS and doesn't necessary reflect the -current development state. - -Unlike symbolic file links provided by NTFS - -- links aren't visible to all applications but only to those the caller chooses -- links disappear when the "session ends" -- doesn't require write access to the link destination -- doesn't require administrator rights (neither for installation nor for use) -- links are filesystem independent so you can create links on fat32 drives, read-only media and network drives -- can link multiple directories on top of a single destination (overlaying) -- can also "virtually" unlink files, thus make them invisible to processes or replace existing files - -There are of course drawbacks - -- will always impose a memory and cpu overhead though hopefully those will be marginal -- becomes active only during the initialization phase of each process so it may not be active at the time dependent dlls are loaded -- introduces a new source of bugs that can cause hard to diagnose problems in affected processes -- may rub antivirus software the wrong way as the used techniques are similar to what some malware does. - -## License - -USVFS is currently licensed under the GPLv3 but this may change in the future. - -## Contributing - -Contributions are very welcome but please notice that since I'm still undecided on -licensing I have to ask all contributors to agree to future licensing changes. diff --git a/libs/usvfs/cmake/config.cmake.in b/libs/usvfs/cmake/config.cmake.in deleted file mode 100644 index b5e85ee..0000000 --- a/libs/usvfs/cmake/config.cmake.in +++ /dev/null @@ -1,6 +0,0 @@ -@PACKAGE_INIT@ - -include ( "${CMAKE_CURRENT_LIST_DIR}/usvfs_x86Targets.cmake" ) -include ( "${CMAKE_CURRENT_LIST_DIR}/usvfs_x64Targets.cmake" ) - -add_library(usvfs::usvfs ALIAS usvfs_x64::usvfs_dll) diff --git a/libs/usvfs/include/usvfs/dllimport.h b/libs/usvfs/include/usvfs/dllimport.h deleted file mode 100644 index c17f648..0000000 --- a/libs/usvfs/include/usvfs/dllimport.h +++ /dev/null @@ -1,31 +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 DLLEXPORT - -#ifdef BUILDING_USVFS_DLL -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT __declspec(dllimport) -#endif - -#endif DLLEXPORT diff --git a/libs/usvfs/include/usvfs/logging.h b/libs/usvfs/include/usvfs/logging.h deleted file mode 100644 index 4647df5..0000000 --- a/libs/usvfs/include/usvfs/logging.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 - -enum class LogLevel : uint8_t -{ - Debug, - Info, - Warning, - Error -}; diff --git a/libs/usvfs/include/usvfs/sharedparameters.h b/libs/usvfs/include/usvfs/sharedparameters.h deleted file mode 100644 index f428dc0..0000000 --- a/libs/usvfs/include/usvfs/sharedparameters.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include "dllimport.h" -#include "usvfsparameters.h" -#include <shared_memory.h> - -namespace usvfs -{ - -class ForcedLibrary -{ -public: - ForcedLibrary(const std::string& processName, const std::string& libraryPath, - const shared::VoidAllocatorT& allocator); - - std::string processName() const; - std::string libraryPath() const; - -private: - shared::StringT m_processName; - shared::StringT m_libraryPath; -}; - -class DLLEXPORT SharedParameters -{ -public: - SharedParameters() = delete; - SharedParameters(const SharedParameters& reference) = delete; - SharedParameters& operator=(const SharedParameters& reference) = delete; - - SharedParameters(const usvfsParameters& reference, - const shared::VoidAllocatorT& allocator); - - usvfsParameters makeLocal() const; - - std::string instanceName() const; - std::string currentSHMName() const; - std::string currentInverseSHMName() const; - void setSHMNames(const std::string& current, const std::string& inverse); - - void setDebugParameters(LogLevel level, CrashDumpsType dumpType, - const std::string& dumpPath, - std::chrono::milliseconds delayProcess); - - std::size_t userConnected(); - std::size_t userDisconnected(); - std::size_t userCount(); - - std::size_t registeredProcessCount() const; - std::vector<DWORD> registeredProcesses() const; - void registerProcess(DWORD pid); - void unregisterProcess(DWORD pid); - - void blacklistExecutable(const std::string& name); - void clearExecutableBlacklist(); - bool executableBlacklisted(const std::string& app, const std::string& cmd) const; - - void addSkipFileSuffix(const std::string& fileSuffix); - void clearSkipFileSuffixes(); - std::vector<std::string> skipFileSuffixes() const; - - void addSkipDirectory(const std::string& directory); - void clearSkipDirectories(); - std::vector<std::string> skipDirectories() const; - - void addForcedLibrary(const std::string& process, const std::string& path); - std::vector<std::string> forcedLibraries(const std::string& processName); - void clearForcedLibraries(); - -private: - using StringAllocatorT = shared::VoidAllocatorT::rebind<shared::StringT>::other; - - using DWORDAllocatorT = shared::VoidAllocatorT::rebind<DWORD>::other; - - using ForcedLibraryAllocatorT = shared::VoidAllocatorT::rebind<ForcedLibrary>::other; - - using ProcessBlacklist = - boost::container::flat_set<shared::StringT, std::less<shared::StringT>, - StringAllocatorT>; - - using ProcessList = - boost::container::flat_set<DWORD, std::less<DWORD>, DWORDAllocatorT>; - - using FileSuffixSkipList = - boost::container::flat_set<shared::StringT, std::less<shared::StringT>, - StringAllocatorT>; - - using DirectorySkipList = - boost::container::flat_set<shared::StringT, std::less<shared::StringT>, - StringAllocatorT>; - - using ForcedLibraries = - boost::container::slist<ForcedLibrary, ForcedLibraryAllocatorT>; - - mutable bi::interprocess_mutex m_mutex; - shared::StringT m_instanceName; - shared::StringT m_currentSHMName; - shared::StringT m_currentInverseSHMName; - bool m_debugMode; - LogLevel m_logLevel; - CrashDumpsType m_crashDumpsType; - shared::StringT m_crashDumpsPath; - std::chrono::milliseconds m_delayProcess; - uint32_t m_userCount; - ProcessBlacklist m_processBlacklist; - ProcessList m_processList; - FileSuffixSkipList m_fileSuffixSkipList; - DirectorySkipList m_directorySkipList; - ForcedLibraries m_forcedLibraries; -}; - -} // namespace usvfs diff --git a/libs/usvfs/include/usvfs/usvfs.h b/libs/usvfs/include/usvfs/usvfs.h deleted file mode 100644 index 6754922..0000000 --- a/libs/usvfs/include/usvfs/usvfs.h +++ /dev/null @@ -1,232 +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 "usvfsparameters.h" - -/* - * Virtual operations: - * - link file - * - link directory (empty) - * - link directory (static) - * - link directory (dynamic) - * - delete file - * - delete directory - * Maybe: - * - rename/move (= copy + delete) - * - copy-on-write semantics (changes to files are done in a separate copy of the - * file, the original is kept on disc but hidden) - */ - -static const unsigned int LINKFLAG_FAILIFEXISTS = - 0x00000001; // if set, linking fails in case of an error -static const unsigned int LINKFLAG_MONITORCHANGES = - 0x00000002; // if set, changes to the source directory after the link operation - // will be updated in the virtual fs. only relevant in static - // link directory operations -static const unsigned int LINKFLAG_CREATETARGET = - 0x00000004; // if set, file creation (including move or copy) operations to - // destination will be redirected to the source. Only one createtarget - // can be set for a destination folder so this flag will replace - // the previous create target. - // If there different create-target have been set for an element and - // one of its ancestors, the inner-most create-target is used -static const unsigned int LINKFLAG_RECURSIVE = - 0x00000008; // if set, directories are linked recursively -static const unsigned int LINKFLAG_FAILIFSKIPPED = - 0x00000010; // if set, linking fails if the file or directory is skipped - // files or directories are skipped depending on whats been added to - // the skip file suffixes or skip directories list in - // the sharedparameters class, those lists are checked during virtual - // linking - -extern "C" -{ - - /** - * removes all virtual mappings - */ - DLLEXPORT void WINAPI usvfsClearVirtualMappings(); - - /** - * link a file virtually - * @note: the directory the destination file resides in has to exist - at least - * virtually. - */ - DLLEXPORT BOOL WINAPI usvfsVirtualLinkFile(LPCWSTR source, LPCWSTR destination, - unsigned int flags); - - /** - * link a directory virtually. This static variant recursively links all files - * individually, change notifications are used to update the information. - * @param failIfExists if true, this call fails if the destination directory exists - * (virtually or physically) - */ - DLLEXPORT BOOL WINAPI usvfsVirtualLinkDirectoryStatic(LPCWSTR source, - LPCWSTR destination, - unsigned int flags); - - /** - * connect to a virtual filesystem as a controller, without hooking the calling - * process. Please note that you can only be connected to one vfs, so this will - * silently disconnect from a previous vfs. - */ - DLLEXPORT BOOL WINAPI usvfsConnectVFS(const usvfsParameters* p); - - /** - * @brief create a new VFS. This is similar to ConnectVFS except it guarantees - * the vfs is reset before use. - */ - DLLEXPORT BOOL WINAPI usvfsCreateVFS(const usvfsParameters* p); - - /** - * disconnect from a virtual filesystem. This removes hooks if necessary - */ - DLLEXPORT void WINAPI usvfsDisconnectVFS(); - - DLLEXPORT void WINAPI usvfsGetCurrentVFSName(char* buffer, size_t size); - - /** - * retrieve a list of all processes connected to the vfs - */ - DLLEXPORT BOOL WINAPI usvfsGetVFSProcessList(size_t* count, LPDWORD processIDs); - - // retrieve a list of all processes connected to the vfs, stores an array - // of `count` elements in `*buffer` - // - // if this returns TRUE and `count` is not 0, the caller must release the buffer - // with `free(*buffer)` - // - // return values: - // - ERROR_INVALID_PARAMETERS: either `count` or `buffer` is NULL - // - ERROR_TOO_MANY_OPEN_FILES: there seems to be way too many usvfs processes - // running, probably some internal error - // - ERROR_NOT_ENOUGH_MEMORY: malloc() failed - // - DLLEXPORT BOOL WINAPI usvfsGetVFSProcessList2(size_t* count, DWORD** buffer); - - /** - * spawn a new process that can see the virtual file system. The signature is - * identical to CreateProcess - */ - DLLEXPORT 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); - - /** - * retrieve a single log message. - * FIXME There is currently no way to unblock from the caller side - * FIXME retrieves log messages from all instances, the logging queue is not separated - */ - DLLEXPORT bool WINAPI usvfsGetLogMessages(LPSTR buffer, size_t size, - bool blocking = false); - - /** - * retrieves a readable representation of the vfs tree - * @param buffer the buffer to write to. this may be null if you only want to - * determine the required buffer size - * @param size pointer to a variable that contains the buffer. After the call - * this value will have been updated to contain the required size, - * even if this is bigger than the buffer size - */ - DLLEXPORT BOOL WINAPI usvfsCreateVFSDump(LPSTR buffer, size_t* size); - - /** - * adds an executable to the blacklist so it doesn't get exposed to the virtual - * file system - * @param executableName name of the executable - */ - DLLEXPORT VOID WINAPI usvfsBlacklistExecutable(LPCWSTR executableName); - - /** - * clears the executable blacklist - */ - DLLEXPORT VOID WINAPI usvfsClearExecutableBlacklist(); - - /** - * adds a file suffix to a list to skip during file linking - * .txt and some_file.txt are both valid file suffixes, - * not to be confused with file extensions - * @param fileSuffix a valid file suffix - */ - DLLEXPORT VOID WINAPI usvfsAddSkipFileSuffix(LPCWSTR fileSuffix); - - /** - * clears the file suffix skip-list - */ - DLLEXPORT VOID WINAPI usvfsClearSkipFileSuffixes(); - - /** - * adds a directory name that will be skipped during directory linking. - * Not a path. Any directory matching the name will be skipped, - * regardless of it's path, for example if .git is added, - * any sub-path or root-path containing a .git directory - * will have the .git directory skipped during directory linking - * @param directory name of the directory - */ - DLLEXPORT VOID WINAPI usvfsAddSkipDirectory(LPCWSTR directory); - - /** - * clears the directory skip-list - */ - DLLEXPORT VOID WINAPI usvfsClearSkipDirectories(); - - /** - * adds a library to be force loaded when the given process is injected - * @param - */ - DLLEXPORT VOID WINAPI usvfsForceLoadLibrary(LPCWSTR processName, LPCWSTR libraryPath); - - /** - * clears all previous calls to ForceLoadLibrary - */ - DLLEXPORT VOID WINAPI usvfsClearLibraryForceLoads(); - - /** - * print debugging info about the vfs. The format is currently not fixed and may - * change between usvfs versions - */ - DLLEXPORT VOID WINAPI usvfsPrintDebugInfo(); - - // #if defined(UNITTEST) || defined(_WINDLL) - DLLEXPORT void WINAPI usvfsInitLogging(bool toLocal = false); - // #endif - - /** - * used internally to initialize a process at startup-time as a "slave". Don't call - * directly - */ - DLLEXPORT void __cdecl InitHooks(LPVOID userData, size_t userDataSize); - - // the instance and shm names are not updated - // - DLLEXPORT void WINAPI usvfsUpdateParameters(usvfsParameters* p); - - DLLEXPORT int WINAPI usvfsCreateMiniDump(PEXCEPTION_POINTERS exceptionPtrs, - CrashDumpsType type, - const wchar_t* dumpPath); - - DLLEXPORT const char* WINAPI usvfsVersionString(); -} diff --git a/libs/usvfs/include/usvfs/usvfs_version.h b/libs/usvfs/include/usvfs/usvfs_version.h deleted file mode 100644 index 03e0cf0..0000000 --- a/libs/usvfs/include/usvfs/usvfs_version.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#define USVFS_VERSION_MAJOR 0 -#define USVFS_VERSION_MINOR 5 -#define USVFS_VERSION_BUILD 7 -#define USVFS_VERSION_REVISION 2 - -#define USVFS_BUILD_STRING "" -#define USVFS_BUILD_WSTRING L"" - -#ifndef USVFS_STRINGIFY -#define USVFS_STRINGIFY(x) USVFS_STRINGIFY_HELPER(x) -#define USVFS_STRINGIFY_HELPER(x) #x -#endif - -#ifndef USVFS_STRINGIFYW -#define USVFS_STRINGIFYW(x) USVFS_STRINGIFYW_HELPER(x) -#define USVFS_STRINGIFYW_HELPER(x) L## #x -#endif - -#define USVFS_VERSION_STRING \ - USVFS_STRINGIFY(USVFS_VERSION_MAJOR) \ - "." USVFS_STRINGIFY(USVFS_VERSION_MINOR) "." USVFS_STRINGIFY( \ - USVFS_VERSION_BUILD) "." USVFS_STRINGIFY(USVFS_VERSION_REVISION) \ - USVFS_BUILD_STRING -#define USVFS_VERSION_WSTRING \ - USVFS_STRINGIFYW(USVFS_VERSION_MAJOR) \ - L"." USVFS_STRINGIFYW(USVFS_VERSION_MINOR) L"." USVFS_STRINGIFYW( \ - USVFS_VERSION_BUILD) L"." USVFS_STRINGIFYW(USVFS_VERSION_REVISION) \ - USVFS_BUILD_WSTRING diff --git a/libs/usvfs/include/usvfs/usvfsparameters.h b/libs/usvfs/include/usvfs/usvfsparameters.h deleted file mode 100644 index 1351d69..0000000 --- a/libs/usvfs/include/usvfs/usvfsparameters.h +++ /dev/null @@ -1,68 +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 "logging.h" -#include <chrono> - -enum class CrashDumpsType : uint8_t -{ - None, - Mini, - Data, - Full -}; - -extern "C" -{ - - // deprecated, use usvfsParameters and usvfsCreateParameters() - // - struct USVFSParameters - { - char instanceName[65]; - char currentSHMName[65]; - char currentInverseSHMName[65]; - bool debugMode{false}; - LogLevel logLevel{LogLevel::Debug}; - CrashDumpsType crashDumpsType{CrashDumpsType::None}; - char crashDumpsPath[260]; - }; - - struct usvfsParameters; - - DLLEXPORT usvfsParameters* usvfsCreateParameters(); - DLLEXPORT usvfsParameters* usvfsDupeParameters(usvfsParameters* p); - DLLEXPORT void usvfsCopyParameters(const usvfsParameters* source, - usvfsParameters* dest); - DLLEXPORT void usvfsFreeParameters(usvfsParameters* p); - - DLLEXPORT void usvfsSetInstanceName(usvfsParameters* p, const char* name); - DLLEXPORT void usvfsSetDebugMode(usvfsParameters* p, BOOL debugMode); - DLLEXPORT void usvfsSetLogLevel(usvfsParameters* p, LogLevel level); - DLLEXPORT void usvfsSetCrashDumpType(usvfsParameters* p, CrashDumpsType type); - DLLEXPORT void usvfsSetCrashDumpPath(usvfsParameters* p, const char* path); - DLLEXPORT void usvfsSetProcessDelay(usvfsParameters* p, int milliseconds); - - DLLEXPORT const char* usvfsLogLevelToString(LogLevel lv); - DLLEXPORT const char* usvfsCrashDumpTypeToString(CrashDumpsType t); -} diff --git a/libs/usvfs/include/usvfs/usvfsparametersprivate.h b/libs/usvfs/include/usvfs/usvfsparametersprivate.h deleted file mode 100644 index 6c2d5f2..0000000 --- a/libs/usvfs/include/usvfs/usvfsparametersprivate.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "usvfsparameters.h" - -struct usvfsParameters -{ - char instanceName[65]; - char currentSHMName[65]; - char currentInverseSHMName[65]; - bool debugMode; - LogLevel logLevel{LogLevel::Debug}; - CrashDumpsType crashDumpsType{CrashDumpsType::None}; - char crashDumpsPath[260]; - int delayProcessMs; - - usvfsParameters(); - usvfsParameters(const usvfsParameters&) = default; - usvfsParameters& operator=(const usvfsParameters&) = default; - - usvfsParameters(const char* instanceName, const char* currentSHMName, - const char* currentInverseSHMName, bool debugMode, LogLevel logLevel, - CrashDumpsType crashDumpsType, const char* crashDumpsPath, - int delayProcessMs); - - usvfsParameters(const USVFSParameters& oldParams); - - void setInstanceName(const char* name); - void setDebugMode(bool debugMode); - void setLogLevel(LogLevel level); - void setCrashDumpType(CrashDumpsType type); - void setCrashDumpPath(const char* path); - void setProcessDelay(int milliseconds); -}; diff --git a/libs/usvfs/licenses/asmjit.txt b/libs/usvfs/licenses/asmjit.txt deleted file mode 100644 index e340bde..0000000 --- a/libs/usvfs/licenses/asmjit.txt +++ /dev/null @@ -1,18 +0,0 @@ -AsmJit - Complete x86/x64 JIT and Remote Assembler for C++ -Copyright (c) 2008-2015, Petr Kobalicek <kobalicek.petr@gmail.com> - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. diff --git a/libs/usvfs/licenses/boost.txt b/libs/usvfs/licenses/boost.txt deleted file mode 100644 index 36b7cd9..0000000 --- a/libs/usvfs/licenses/boost.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/libs/usvfs/licenses/cppformat.txt b/libs/usvfs/licenses/cppformat.txt deleted file mode 100644 index 8db9a7c..0000000 --- a/libs/usvfs/licenses/cppformat.txt +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) 2012 - 2015, Victor Zverovich - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/usvfs/licenses/googletest.txt b/libs/usvfs/licenses/googletest.txt deleted file mode 100644 index 1941a11..0000000 --- a/libs/usvfs/licenses/googletest.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2008, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/usvfs/licenses/qt.txt b/libs/usvfs/licenses/qt.txt deleted file mode 100644 index 87be995..0000000 --- a/libs/usvfs/licenses/qt.txt +++ /dev/null @@ -1,174 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - - The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. - Contact: http://www.qt.io/licensing/ - - You may use, distribute and copy the Qt GUI Toolkit under the terms of - GNU Lesser General Public License version 3, which is displayed below. - This license makes reference to the version 3 of the GNU General - Public License, which you can find in the LICENSE.GPLv3 file. - -------------------------------------------------------------------------- - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> -Everyone is permitted to copy and distribute verbatim copies of this -licensedocument, but changing it is not allowed. - -This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - -0. Additional Definitions. - - As used herein, “this License” refers to version 3 of the GNU Lesser -General Public License, and the “GNU GPL” refers to version 3 of the -GNU General Public License. - - “The Library” refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An “Application” is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A “Combined Work” is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the “Linked -Version”. - - The “Minimal Corresponding Source” for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The “Corresponding Application Code” for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - -1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - -2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - -3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this - license document. - -4. Combined Works. - - You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. - - e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) - -5. Combined Libraries. - - You may place library facilities that are a work based on the Library -side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of - it is a work based on the Library, and explaining where to find - the accompanying uncombined form of the same work. - -6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License “or any later version” applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, -you may choose any version of the GNU Lesser General Public License -ever published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the Library. diff --git a/libs/usvfs/licenses/spdlog.txt b/libs/usvfs/licenses/spdlog.txt deleted file mode 100644 index 1dfac98..0000000 --- a/libs/usvfs/licenses/spdlog.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Gabi Melman. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/libs/usvfs/licenses/udis86.txt b/libs/usvfs/licenses/udis86.txt deleted file mode 100644 index 80f537d..0000000 --- a/libs/usvfs/licenses/udis86.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2002-2012, Vivek Thampi <vivek.mt@gmail.com> -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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", - ¶meters, 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 diff --git a/libs/usvfs/test/CMakeLists.txt b/libs/usvfs/test/CMakeLists.txt deleted file mode 100644 index c34b31f..0000000 --- a/libs/usvfs/test/CMakeLists.txt +++ /dev/null @@ -1,74 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -include(CMakeParseArguments) - -#! usvfs_set_test_properties -# -# this function sets the following properties on the given executable or shared -# library test target: -# - OUTPUT_NAME to add arch-specific prefix -# - OUTPUT_DIRECTORY to put the test executable or shared library in the right location -# - FOLDER to organize the VS solution layout -# -# \param:FOLDER if present, specifies the subfolder to use in the solution -# -function(usvfs_set_test_properties TARGET) - cmake_parse_arguments(USVFS_TEST "" "FOLDER" "" ${ARGN}) - if (NOT DEFINED USVFS_TEST_FOLDER) - set(folder "tests") - else() - set(folder "tests/${USVFS_TEST_FOLDER}") - endif() - set_target_properties(${TARGET} - PROPERTIES - FOLDER ${folder} - RUNTIME_OUTPUT_NAME ${TARGET}${ARCH_POSTFIX} - RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_TEST_BINDIR} - RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_TEST_BINDIR} - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${USVFS_TEST_BINDIR} - ) -endfunction() - -#! usvfs_target_link_usvfs -# -# add a target link between the given target and the usvfs shared library with delay -# loading -# -function(usvfs_target_link_usvfs TARGET) - target_link_libraries(${TARGET} PRIVATE usvfs_dll) - target_link_options(${TARGET} PRIVATE "/DELAYLOAD:usvfs${ARCH_POSTFIX}.dll") -endfunction() - - -file(GLOB directories LIST_DIRECTORIES true "*") - -# this goes through all the directories and -# -# 1. add them if there is a CMakeLists.txt inside -# 2. add correspondings tests (for CTest) for BOTH x86 and x64 when possible -# -foreach(directory ${directories}) - if(NOT(IS_DIRECTORY ${directory})) - continue() - endif() - - if(NOT(EXISTS ${directory}/CMakeLists.txt)) - continue() - endif() - - add_subdirectory(${directory}) - - get_filename_component(dirname ${directory} NAME) - if((dirname STREQUAL "test_utils") OR (dirname STREQUAL "gtest_utils")) - continue() - endif() - - add_test(NAME ${dirname}_x64 - COMMAND ${USVFS_TEST_BINDIR}/${dirname}_x64.exe - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) - add_test(NAME ${dirname}_x86 - COMMAND ${USVFS_TEST_BINDIR}/${dirname}_x86.exe - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - ) -endforeach() diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/data/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/data/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/data/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/subdocs/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/subdocs/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/subdocs/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/data/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/data/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/data/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/subdocs/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/subdocs/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/subdocs/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp deleted file mode 100644 index 9a79d16..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include <filesystem> -#include <fstream> - -std::filesystem::path storages_path; -std::filesystem::path hudpainter_path; -bool has_mo2; - -// See below main() for implementation. -void request_directory(std::filesystem::path); -std::filesystem::path restrict_path(std::filesystem::path); -std::vector<std::filesystem::path> get_files(); -std::string read_file(std::filesystem::path); -void write_file(std::filesystem::path); - -// Expected path when not using MO2 -int main() -{ - // RedFileSystem plugin (DLL) is loaded. - has_mo2 = GetModuleHandle(TEXT("usvfs_x64.dll")) != nullptr; - - // Setup paths - auto path = std::filesystem::absolute("."); // <GAME>\bin\x64 - auto game_path = path.parent_path().parent_path(); // <GAME> - - storages_path = game_path / "r6" / "storages"; // <GAME>\r6\storages - - // Create storages directory if it is no present. - request_directory(storages_path); // <GAME>\r6\storages - - // Game is running... - - // HUD Painter request its storage endpoint. - hudpainter_path = storages_path / "HUDPainter"; - request_directory(hudpainter_path); // <GAME>\r6\storages\HUDPainter - - // HUD Painter request a file (present in archive). - auto default_path = - restrict_path("DEFAULT.json"); // <GAME>\r6\storages\HUDPainter\DEFAULT.json - - // HUD Painter read file in `default_path` with success. - auto data = read_file(default_path); // <GAME>\r6\storages\HUDPainter\DEFAULT.json - - // HUD Painter request another file. - auto test_path = - restrict_path("TEST.json"); // <GAME>\r6\storages\HUDPainter\TEST.json - - // (Bug A) HUD Painter write file in `test_path`. - write_file(test_path); // <GAME>\r6\storages\HUDPainter\TEST.json - // file is created with workaround, not created otherwise. - - // (Bug B) HUD Painter request list of files. - // TEST.json is successfully created when using workaround. - auto files = get_files(); // [0] <GAME>\r6\storages\HUDPainter\DEFAULT.json - - // files.size() == 1 - - // Expect: - // files.size() == 2 - // - // [0] <GAME>\r6\storages\HUDPainter\DEFAULT.json - // [1] <GAME>\r6\storages\HUDPainter\TEST.json -} - -// Create directory if it is no present -void request_directory(std::filesystem::path path) -{ - bool is_present = std::filesystem::exists(path); - - if (is_present) { - return; - } - std::filesystem::create_directory(path); -} - -// Resolve path for HUDPainter (security layer) -std::filesystem::path restrict_path(std::filesystem::path path) -{ - auto real_path = std::filesystem::weakly_canonical(hudpainter_path / path); - - // Workaround when using MO2 - if (has_mo2) { - return real_path; - } - // Expected implementation without MO2 - if (real_path.string().find(hudpainter_path.string() + "\\") != 0) { - // "throw" error - } - return real_path; -} - -// List files in storage of HUDPainter. -std::vector<std::filesystem::path> get_files() -{ - std::vector<std::filesystem::path> files; - auto entries = std::filesystem::directory_iterator(hudpainter_path); - - for (const auto& entry : entries) { - if (entry.is_regular_file()) { - auto file_name = entry.path().filename(); - auto file_path = - hudpainter_path / - file_name; // Culprit of bug B. When using entry.path() directly, it works. - - files.emplace_back(file_path); - } - } - return files; -} - -std::string read_file(std::filesystem::path path) -{ - std::ifstream stream; - - // With workaround: - // std::filesystem::create_directories(path.parent_path()); - stream.open(path); - if (!stream.is_open()) { - return ""; - } - std::stringstream data; - - data << stream.rdbuf(); - stream.close(); - return data.str(); -} - -void write_file(std::filesystem::path path) -{ - std::ofstream stream; - - // With workaround: - // std::filesystem::create_directories(path.parent_path()); - stream.open(path, std::ios_base::trunc); - if (!stream.is_open()) { - return; - } - stream << "<DATA>"; - stream.close(); -} diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json deleted file mode 100644 index dd85d0a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "propertiesDefault": [ - { - "name": "MainColors.Red", - "red": 1.1761, - "green": 0.3809, - "blue": 0.3476, - "alpha": 1 - } - ] -} diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json deleted file mode 100644 index 0967ef4..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json b/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json deleted file mode 100644 index dd85d0a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "propertiesDefault": [ - { - "name": "MainColors.Red", - "red": 1.1761, - "green": 0.3809, - "blue": 0.3476, - "alpha": 1 - } - ] -} diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip deleted file mode 100644 index 09a24fd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip +++ /dev/null @@ -1 +0,0 @@ -doc.skip in data/docs diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip deleted file mode 100644 index 4b8bde6..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip +++ /dev/null @@ -1 +0,0 @@ -doc.skip in mod1/docs diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip deleted file mode 100644 index 1d1857a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip +++ /dev/null @@ -1 +0,0 @@ -readme.skip in mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip deleted file mode 100644 index 0062dba..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip +++ /dev/null @@ -1 +0,0 @@ -readme.skip in overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip deleted file mode 100644 index 09a24fd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip +++ /dev/null @@ -1 +0,0 @@ -doc.skip in data/docs diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip deleted file mode 100644 index 4b8bde6..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip +++ /dev/null @@ -1 +0,0 @@ -doc.skip in mod1/docs diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip deleted file mode 100644 index 1d1857a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip +++ /dev/null @@ -1 +0,0 @@ -readme.skip in mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt b/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rcopyme4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rcopyme4.txt deleted file mode 100644 index d1e44e9..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rcopyme4.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rcopyme4.txt original contents diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfile0.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfile0.txt deleted file mode 100644 index 9830306..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfile0.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfile0.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfiledeletewrite.txt deleted file mode 100644 index 2743678..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfiledeletewrite.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfilerewrite.txt deleted file mode 100644 index bdff476..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfilerewrite.txt rewrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0.txt deleted file mode 100644 index a52b6be..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0.txt +++ /dev/null @@ -1 +0,0 @@ -root0 original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0w.txt deleted file mode 100644 index c177584..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root0w.txt +++ /dev/null @@ -1 +0,0 @@ -root0w original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1.txt deleted file mode 100644 index 06dc692..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1.txt +++ /dev/null @@ -1 +0,0 @@ -root1 original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1w.txt deleted file mode 100644 index 45172aa..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount.postmortem/root1w.txt +++ /dev/null @@ -1 +0,0 @@ -root1w original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rcopyme4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rcopyme4.txt deleted file mode 100644 index d1e44e9..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rcopyme4.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rcopyme4.txt original contents diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfile0.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfile0.txt deleted file mode 100644 index 9830306..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfile0.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfile0.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledelete.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledelete.txt deleted file mode 100644 index c19828f..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledelete.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfiledelete.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledeletewrite.txt deleted file mode 100644 index 93861cc..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfiledeletewrite.txt orignal file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfileoldname.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfileoldname.txt deleted file mode 100644 index 9376ea1..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfileoldname.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfileoldname.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfilerewrite.txt deleted file mode 100644 index 499775d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/rfolder/rfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfilerewrite.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0.txt deleted file mode 100644 index a52b6be..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0.txt +++ /dev/null @@ -1 +0,0 @@ -root0 original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0w.txt deleted file mode 100644 index c177584..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root0w.txt +++ /dev/null @@ -1 +0,0 @@ -root0w original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1.txt deleted file mode 100644 index 06dc692..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1.txt +++ /dev/null @@ -1 +0,0 @@ -root1 original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1w.txt deleted file mode 100644 index 45172aa..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/mount/root1w.txt +++ /dev/null @@ -1 +0,0 @@ -root1w original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfile.txt deleted file mode 100644 index 227256a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder1\mfile.txt by mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfilew.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfilew.txt deleted file mode 100644 index 420589d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfilew.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder1\mfilew.txt by mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder3/mdeep3/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder3/mdeep3/mfile.txt deleted file mode 100644 index 6a7a0c2..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder3/mdeep3/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder3\mdeep3\mfile.txt by mod1 (to be hidden by mod3) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1.txt deleted file mode 100644 index 7d5dc0e..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1w.txt deleted file mode 100644 index 1838cdf..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1w.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod1w diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder2/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder2/mfile.txt deleted file mode 100644 index 8ff096e..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder2/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder2\mfile.txt by mod2 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfile.txt deleted file mode 100644 index c0f0f2c..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove.txt deleted file mode 100644 index e35f1f3..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove2p.txt deleted file mode 100644 index cc572dc..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove2p.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite.txt deleted file mode 100644 index f699360..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite2p.txt deleted file mode 100644 index 9eae9c5..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite2p.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilemoveover.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilemoveover.txt deleted file mode 100644 index 88a6d08..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilemoveover.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfilemoveover.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfileoverwrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfileoverwrite.txt deleted file mode 100644 index 61b92a6..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfileoverwrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfileoverwrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilerewrite.txt deleted file mode 100644 index 727a48f..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfilerewrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mod2.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mod2.txt deleted file mode 100644 index 49bbbf1..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod2/mod2.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod2 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder3/mdeep3/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder3/mdeep3/mfile.txt deleted file mode 100644 index fac8213..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder3/mdeep3/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder3\mdeep3\mfile.txt by mod3 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder4/mfile.txt deleted file mode 100644 index 5e6ca00..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod3 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mod3.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mod3.txt deleted file mode 100644 index 47fbacd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod3/mod3.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod3 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfile.txt deleted file mode 100644 index c67ecfe..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletemove.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletemove.txt deleted file mode 100644 index 00cc0cd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletemove.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfiledeletemove.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletewrite.txt deleted file mode 100644 index 083a6b8..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfiledeletewrite.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilemoveover.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilemoveover.txt deleted file mode 100644 index a8a93c2..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilemoveover.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfilemoveover.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfileoverwrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfileoverwrite.txt deleted file mode 100644 index fc6d42f..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfileoverwrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfileoverwrite.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilerewrite.txt deleted file mode 100644 index a3b8ced..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfilerewrite.txt rewrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/.empty b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/.empty deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/.empty +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfile1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfile1.txt deleted file mode 100644 index bf76f1d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfile1.txt +++ /dev/null @@ -1 +0,0 @@ -newfile1.txt nonrecursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfolder1/newfile1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfolder1/newfile1.txt deleted file mode 100644 index 4802577..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfolder1/newfile1.txt +++ /dev/null @@ -1 +0,0 @@ -newfile1.txt nonrecursive overwrite subfolder diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder2/newfile2.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder2/newfile2.txt deleted file mode 100644 index a4fa7fc..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder2/newfile2.txt +++ /dev/null @@ -1 +0,0 @@ -newfile2.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3.txt deleted file mode 100644 index 933eb0c..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3.txt +++ /dev/null @@ -1 +0,0 @@ -newfile3.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3e.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3e.txt deleted file mode 100644 index 5e5286a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3e.txt +++ /dev/null @@ -1 +0,0 @@ -newfile3e.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletemove2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletemove2p.txt deleted file mode 100644 index 6b85a92..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletemove2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfiledeletemove2p.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletewrite2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletewrite2p.txt deleted file mode 100644 index 64cac84..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletewrite2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfiledeletewrite2p.txt overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4.txt deleted file mode 100644 index cba5361..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4.txt +++ /dev/null @@ -1 +0,0 @@ -newfile4.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4e.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4e.txt deleted file mode 100644 index eb90089..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4e.txt +++ /dev/null @@ -1 +0,0 @@ -newfile4e.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4enr.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4enr.txt deleted file mode 100644 index f8b5f9f..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4enr.txt +++ /dev/null @@ -1 +0,0 @@ -newfile4enr.txt nonrecursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4.txt deleted file mode 100644 index 06edab8..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4.txt +++ /dev/null @@ -1 +0,0 @@ -epnewfile4.txt nonrecursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4r.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4r.txt deleted file mode 100644 index 8fd183d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4r.txt +++ /dev/null @@ -1 +0,0 @@ -epnewfile4r.txt recursive overwrite diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4p/rcopyme4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4p/rcopyme4.txt deleted file mode 100644 index d1e44e9..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4p/rcopyme4.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rcopyme4.txt original contents diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/rcopyme4.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/rcopyme4.txt deleted file mode 100644 index d1e44e9..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/rcopyme4.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rcopyme4.txt original contents diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/rfolder/rfilenewname.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/rfolder/rfilenewname.txt deleted file mode 100644 index 9376ea1..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/rfolder/rfilenewname.txt +++ /dev/null @@ -1 +0,0 @@ -rfolder\rfileoldname.txt original file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1.txt deleted file mode 100644 index 5aa129b..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1.txt +++ /dev/null @@ -1 +0,0 @@ -root1 source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1w.txt deleted file mode 100644 index 0c04aa5..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root1w.txt +++ /dev/null @@ -1 +0,0 @@ -root1w source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2.txt deleted file mode 100644 index 4ac40bb..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2.txt +++ /dev/null @@ -1 +0,0 @@ -root2 source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2w.txt deleted file mode 100644 index 83ddb42..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source.postmortem/root2w.txt +++ /dev/null @@ -1 +0,0 @@ -root2w source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfile.txt deleted file mode 100644 index 227256a..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder1\mfile.txt by mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfilew.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfilew.txt deleted file mode 100644 index 420589d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfilew.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder1\mfilew.txt by mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder3/mdeep3/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder3/mdeep3/mfile.txt deleted file mode 100644 index 6a7a0c2..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mfolder3/mdeep3/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder3\mdeep3\mfile.txt by mod1 (to be hidden by mod3) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1.txt deleted file mode 100644 index 7d5dc0e..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod1 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1w.txt deleted file mode 100644 index 1838cdf..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod1/mod1w.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod1w diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder2/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder2/mfile.txt deleted file mode 100644 index 8ff096e..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder2/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder2\mfile.txt by mod2 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfile.txt deleted file mode 100644 index c0f0f2c..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove.txt deleted file mode 100644 index e35f1f3..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove2p.txt deleted file mode 100644 index cc572dc..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove2p.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite.txt deleted file mode 100644 index f699360..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite2p.txt deleted file mode 100644 index 9eae9c5..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite2p.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilemoveover.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilemoveover.txt deleted file mode 100644 index 88a6d08..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilemoveover.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfilemoveover.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfileoverwrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfileoverwrite.txt deleted file mode 100644 index 61b92a6..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfileoverwrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfileoverwrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilerewrite.txt deleted file mode 100644 index 727a48f..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfilerewrite.txt by mod2 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mod2.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mod2.txt deleted file mode 100644 index 49bbbf1..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod2/mod2.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod2 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder3/mdeep3/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder3/mdeep3/mfile.txt deleted file mode 100644 index fac8213..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder3/mdeep3/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder3\mdeep3\mfile.txt by mod3 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder4/mfile.txt deleted file mode 100644 index 5e6ca00..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod3 (to be hidden by mod4) diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mod3.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mod3.txt deleted file mode 100644 index 47fbacd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod3/mod3.txt +++ /dev/null @@ -1 +0,0 @@ -hello mod3 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfile.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfile.txt deleted file mode 100644 index c67ecfe..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfile.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfile.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove.txt deleted file mode 100644 index 657f07d..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove2p.txt deleted file mode 100644 index cfd08dd..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletemove2p.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite.txt deleted file mode 100644 index a17d129..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite2p.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite2p.txt deleted file mode 100644 index b198d1c..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite2p.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfiledeletewrite2p.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilemoveover.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilemoveover.txt deleted file mode 100644 index b395908..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilemoveover.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4/mfilemoveover.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfileoverwrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfileoverwrite.txt deleted file mode 100644 index 405c0c1..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfileoverwrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfileoverwrite.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilerewrite.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilerewrite.txt deleted file mode 100644 index def973c..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilerewrite.txt +++ /dev/null @@ -1 +0,0 @@ -mfolder4\mfilerewrite.txt by mod4 diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/overwrite/.empty b/libs/usvfs/test/fixtures/usvfs_test/basic/source/overwrite/.empty deleted file mode 100644 index e69de29..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/overwrite/.empty +++ /dev/null diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1.txt deleted file mode 100644 index 5aa129b..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1.txt +++ /dev/null @@ -1 +0,0 @@ -root1 source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1w.txt deleted file mode 100644 index 0c04aa5..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root1w.txt +++ /dev/null @@ -1 +0,0 @@ -root1w source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2.txt deleted file mode 100644 index 4ac40bb..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2.txt +++ /dev/null @@ -1 +0,0 @@ -root2 source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2w.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2w.txt deleted file mode 100644 index 83ddb42..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/source/root2w.txt +++ /dev/null @@ -1 +0,0 @@ -root2w source file diff --git a/libs/usvfs/test/fixtures/usvfs_test/basic/vfs_mappings.txt b/libs/usvfs/test/fixtures/usvfs_test/basic/vfs_mappings.txt deleted file mode 100644 index cc755b4..0000000 --- a/libs/usvfs/test/fixtures/usvfs_test/basic/vfs_mappings.txt +++ /dev/null @@ -1,26 +0,0 @@ -# mapdir <optional relative path under mount> -# <relative path under source to mount> -# ... -mapdir - mod1 - mod2 - mod3 - mod4 - -# mapdircreate <optional relative path under mount> -# <relative path under source to mount> -# ... -mapdircreate - overwrite - -# mapfile <optional relative path under mount> -# <relative path under source to mount> -# ... -mapfile root1.txt - root1.txt -mapfile root1w.txt - root1w.txt -mapfile root2.txt - root2.txt -mapfile root2w.txt - root2w.txt diff --git a/libs/usvfs/test/gtest_utils/CMakeLists.txt b/libs/usvfs/test/gtest_utils/CMakeLists.txt deleted file mode 100644 index 8dcda70..0000000 --- a/libs/usvfs/test/gtest_utils/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -add_library(gtest_utils STATIC - gtest_utils.cpp - gtest_utils.h -) -set_target_properties(gtest_utils PROPERTIES FOLDER tests) -target_link_libraries(gtest_utils PUBLIC GTest::gtest) -target_include_directories(gtest_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/libs/usvfs/test/gtest_utils/gtest_utils.cpp b/libs/usvfs/test/gtest_utils/gtest_utils.cpp deleted file mode 100644 index f292dfd..0000000 --- a/libs/usvfs/test/gtest_utils/gtest_utils.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "gtest_utils.h" - -// this file is shared by both usvfs_global_test and usvfs_global_test_runner -// - -#include <filesystem> -#include <format> -#include <fstream> - -#include <boost/algorithm/string.hpp> - -#include <gtest/gtest.h> - -std::string trimmed(std::string content) -{ - boost::algorithm::trim(content); - return content; -} - -// small utility to read files -std::optional<std::string> read_content(const std::filesystem::path& path, - bool trim = true) -{ - std::ifstream ifs(path, std::ios::binary | std::ios::ate); - - if (!ifs) { - return {}; - } - - const auto count = ifs.tellg(); - - std::string buffer(static_cast<std::size_t>(count), '\0'); - - ifs.seekg(0, std::ios::beg); - ifs.read(buffer.data(), count); - - if (trim) { - boost::algorithm::trim(buffer); - } - - return buffer; -} - -::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, - const std::filesystem::path& actual, - bool content) -{ - std::vector<std::string> failure_messages; - std::vector<std::filesystem::path> in_both; - - // iterate left, check on right - for (const auto& it : std::filesystem::recursive_directory_iterator{expected}) { - const auto relpath = relative(it.path(), expected); - if (!exists(actual / relpath)) { - failure_messages.push_back( - std::format("{} expected but not found", relpath.string())); - } else { - in_both.push_back(relpath); - } - } - - // iterate right, check on left - for (const auto& it : std::filesystem::recursive_directory_iterator{actual}) { - const auto relpath = relative(it.path(), actual); - if (!exists(expected / relpath)) { - failure_messages.push_back( - std::format("{} found but not expected", relpath.string())); - } - } - - // check contents - if (content) { - for (const auto& relpath : in_both) { - const auto expected_path = expected / relpath, actual_path = actual / relpath; - - if (is_directory(expected_path) != is_directory(actual_path)) { - failure_messages.push_back( - std::format("{} type mismatch, expected {} but found {}", relpath.string(), - is_directory(expected_path) ? "directory" : "file", - is_directory(expected_path) ? "file" : "directory")); - continue; - } - - if (is_directory(expected_path)) { - continue; - } - - if (read_content(expected_path) != read_content(actual_path)) { - failure_messages.push_back( - std::format("{} content mismatch", relpath.string())); - } - } - } - - if (failure_messages.empty()) { - return ::testing::AssertionSuccess(); - } - - return ::testing::AssertionFailure() - << "\n" - << boost::algorithm::join(failure_messages, "\n") << "\n"; -} - -::testing::AssertionResult AssertContentEquals(std::string_view expected, - const std::filesystem::path& path, - bool trim) -{ - const auto content = read_content(path, trim); - - if (!content) { - return ::testing::AssertionFailure() - << "failed to open path '" << path.string() << "'"; - } - - if (*content != expected) { - return ::testing::AssertionFailure() - << "mismatch content for '" << path.string() << "', expected '" << expected - << "', found '" << *content << "'"; - } - - return ::testing::AssertionSuccess(); -} diff --git a/libs/usvfs/test/gtest_utils/gtest_utils.h b/libs/usvfs/test/gtest_utils/gtest_utils.h deleted file mode 100644 index 1dba70c..0000000 --- a/libs/usvfs/test/gtest_utils/gtest_utils.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -// this file is shared by both usvfs_global_test and usvfs_global_test_runner -// - -#include <filesystem> - -#include <gtest/gtest.h> - -::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, - const std::filesystem::path& actual, - bool content = true); - -::testing::AssertionResult AssertContentEquals(std::string_view expected, - const std::filesystem::path& path, - bool trim = true); - -// macro to assert that the contents of two directories are identical - directories are -// compared recursively and file contents are compared (excluding extra spaces or lines -// in file) -// -#define ASSERT_DIRECTORY_EQ(Expected, Actual) \ - ASSERT_TRUE(AssertDirectoryEquals(Expected, Actual)) - -// macro to assert that the contents of two files are identical (excluding extra space -// or lines in file) -// -#define ASSERT_CONTENT_EQ(Expected, Path) \ - ASSERT_TRUE(AssertContentEquals(Expected, Path)) diff --git a/libs/usvfs/test/shared_test/CMakeLists.txt b/libs/usvfs/test/shared_test/CMakeLists.txt deleted file mode 100644 index c5a42ea..0000000 --- a/libs/usvfs/test/shared_test/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -add_executable(shared_test main.cpp) -usvfs_set_test_properties(shared_test) -target_link_libraries(shared_test PRIVATE test_utils GTest::gtest GTest::gtest_main) diff --git a/libs/usvfs/test/shared_test/main.cpp b/libs/usvfs/test/shared_test/main.cpp deleted file mode 100644 index c6b5be6..0000000 --- a/libs/usvfs/test/shared_test/main.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include <boost/interprocess/containers/string.hpp> -#include <boost/interprocess/shared_memory_object.hpp> -#include <boost/predef.h> -#include <gtest/gtest.h> -#include <shared_memory.h> -#include <spdlog/sinks/stdout_sinks.h> -#include <wildcard.h> -#include <windows_sane.h> - -#define PRIVATE public -#include <directory_tree.h> -#undef PRIVATE - -#include <tree_container.h> - -using namespace usvfs::shared; - -using namespace boost::interprocess; - -static const char g_SHMName[] = "treetest_shm"; -static const char TreeName[] = "treetest_tree"; - -typedef DirectoryTree<int> TreeType; -typedef TreeContainer<TreeType> ContainerType; - -typedef boost::container::scoped_allocator_adaptor< - boost::interprocess::allocator<void, SegmentManagerT>> - VoidAllocator; -typedef VoidAllocator::rebind<char>::other CharAllocator; -typedef basic_string<char, std::char_traits<char>, CharAllocator> SHMString; -typedef DirectoryTree<SHMString> ComplexTreeType; -typedef TreeContainer<ComplexTreeType> ComplexContainerType; - -template <> -struct usvfs::shared::SHMDataCreator<int, int> -{ - static int create(int source, const VoidAllocatorT&) { return source; } -}; - -template <> -inline int usvfs::shared::createDataEmpty<int>(const typename VoidAllocatorT&) -{ - return 0; -} - -template <> -inline void usvfs::shared::dataAssign<int>(int& destination, const int& source) -{ - destination = source; -} - -template <> -inline SHMString -usvfs::shared::createDataEmpty<SHMString>(const typename VoidAllocatorT& allocator) -{ - return SHMString("", allocator); -} - -template <> -inline void usvfs::shared::dataAssign<SHMString>(SHMString& destination, - const SHMString& source) -{ - destination.assign(source.c_str()); -} - -static std::shared_ptr<spdlog::logger> logger() -{ - std::shared_ptr<spdlog::logger> result = spdlog::get("test"); - if (result.get() == nullptr) { - result = spdlog::stdout_logger_mt("test"); - } - return result; -} - -TEST(WildcardTest, MatchWildcard) -{ - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("a*"))); - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("a*c"))); - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("a?c"))); - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("abc"))); - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("abc*"))); - - EXPECT_TRUE(wildcard::Match("abc", "*.*")); - EXPECT_TRUE(wildcard::Match("abc.def", "*")); - EXPECT_TRUE(wildcard::Match(TEXT("abc"), TEXT("*.*"))); - EXPECT_TRUE(wildcard::Match(TEXT("abc.def"), TEXT("*"))); - - EXPECT_NE(nullptr, wildcard::PartialMatch("abc", "*.*")); - EXPECT_NE(nullptr, wildcard::PartialMatch("abc.def", "*")); - EXPECT_EQ('\0', *wildcard::PartialMatch("abc", "*.*")); - EXPECT_EQ('\0', *wildcard::PartialMatch("abc.def", "*")); - - EXPECT_FALSE(wildcard::Match(TEXT("abc"), TEXT("b*"))); -} - -TEST(DirectoryTreeTest, SimpleTreeInit) -{ - EXPECT_NO_THROW({ - ContainerType tree(g_SHMName, 4096); - TreeType::NodePtrT p = tree.addFile(R"(C:\temp\test.txt)", 42, false); - EXPECT_NE(TreeType::NodePtrT(), p); - }); -} - -TEST(DirectoryTreeTest, FindNode) -{ - shared_memory_object::remove(g_SHMName); - ContainerType tree(g_SHMName, 64 * 1024); - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp\bla)", 0x42, 0, false)); - - EXPECT_NE(nullptr, tree->findNode(R"(C:\temp)").get()); - EXPECT_EQ(nullptr, tree->findNode(R"(C:\temp\bla\blubb)").get()); -} - -struct TestVisitor -{ - TreeType::NodePtrT lastNode; - bool flag40{false}; - - void operator()(const TreeType::NodePtrT& node) - { - lastNode = node; - flag40 = node->hasFlag(0x40); - logger()->debug("{0} - {1}", lastNode->name(), flag40); - // BOOST_LOG_SEV(globalLogger::get(), LogLevel::Debug) << lastNode->name() << " - " - // << flag40; - } -}; - -TEST(DirectoryTreeTest, VisitPath) -{ - shared_memory_object::remove(g_SHMName); - ContainerType tree(g_SHMName, 64 * 1024); - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp\bla)", 1, 0x40, false)); - - TestVisitor visitor; - - tree->visitPath(R"(C:\temp\bla\blubb)", - TreeType::VisitorFunction([&](const TreeType::NodePtrT& node) { - visitor(node); - })); - EXPECT_TRUE(visitor.flag40); - EXPECT_EQ("bla", visitor.lastNode->name()); -} - -TEST(DirectoryTreeTest, WildCardFind) -{ - shared_memory_object::remove(g_SHMName); - EXPECT_NO_THROW({ - ContainerType tree(g_SHMName, 64 * 1024); - - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp)", 1, FLAG_DIRECTORY, false)); - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp\abc)", 1, 0, false)); - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp\abd)", 2, 0, false)); - EXPECT_NE(nullptr, tree.addFile(R"(C:\temp\ace)", 3, 0, false)); - - EXPECT_EQ(3, tree->find(R"(C:\temp\*)").size()); - EXPECT_NE(nullptr, tree->node("C:")); - EXPECT_EQ(1, tree->node("C:")->find("*").size()); - EXPECT_NE(nullptr, tree->node("C:")->node("temp")); - EXPECT_EQ(3, tree->node("C:")->node("temp")->find("*").size()); // alternative - // search on the top-level - EXPECT_EQ(1, tree->find("*").size()); - // * should work - EXPECT_EQ(2, tree->find(R"(C:\temp\ab*)").size()); - // * does not match directory separators - EXPECT_EQ(0, tree->find("*ab*").size()); - // matches only the directory itself - EXPECT_EQ(1, tree->find(R"(C:\temp*)").size()); - }); -} - -TEST(DirectoryTreeTest, SHMAllocation) -{ - EXPECT_NO_THROW({ - ContainerType create(g_SHMName, 64 * 1024); - - { // creation - create.addFile(R"(C:\temp\abc)", 1, false); - create.addFile(R"(C:\temp\abd)", 2, false); - create.addFile(R"(C:\temp\ace)", 3, false); - } - - { // access - ContainerType access(g_SHMName, 64 * 1024); - EXPECT_NE(nullptr, access.get()); - std::vector<TreeType::NodePtrT> res = access->find(R"(C:\temp\*)"); - EXPECT_EQ(3, res.size()); // matches the three files - EXPECT_EQ(access->m_Self.lock().get(), access->node("C:")->parent().get()); - } - }); -} - -TEST(DirectoryTreeTest, SHMAllocationError) -{ - EXPECT_NO_THROW({ - try { - ContainerType tree(g_SHMName, 4096); - int c = 0; - for (char i = 'a'; i <= 'z'; ++i) { - for (char j = 'a'; j <= 'z'; ++j) { - std::string name = std::string(R"(C:\temp\)") + i + j; - tree.addFile(name, ++c, false); - } - } - - EXPECT_EQ(1, tree->node("C:")->node("temp")->node("aa", MissingThrow)->data()); - EXPECT_EQ(26, tree->node("C:")->node("temp")->node("az", MissingThrow)->data()); - } catch (const std::exception& e) { - logger()->error("{0}", e.what()); - // BOOST_LOG_SEV(globalLogger::get(), LogLevel::Error) << e.what(); - throw; - } - }); -} - -TEST(DirectoryTreeTest, SHMAllocationErrorComplex) -{ - EXPECT_NO_THROW({ - try { - ComplexContainerType tree(g_SHMName, 4096); - SHMString str = tree.create("gaga"); - for (char i = 'a'; i <= 'z'; ++i) { - for (char j = 'a'; j <= 'z'; ++j) { - std::string name = std::string(R"(C:\temp\)") + i + j; - tree.addFile(name, str, false); - } - } - EXPECT_STREQ( - str.c_str(), - tree->node("C:")->node("temp")->node("aa", MissingThrow)->data().c_str()); - EXPECT_STREQ( - str.c_str(), - tree->node("C:")->node("temp")->node("az", MissingThrow)->data().c_str()); - } catch (const std::exception& e) { - logger()->error("{}", e.what()); - throw; - } - }); -} - -int main(int argc, char** argv) -{ - auto logger = spdlog::stdout_logger_mt("usvfs"); - logger->set_level(spdlog::level::warn); - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/test_utils/CMakeLists.txt b/libs/usvfs/test/test_utils/CMakeLists.txt deleted file mode 100644 index 88c75d6..0000000 --- a/libs/usvfs/test/test_utils/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -add_library(test_utils STATIC - test_helpers.cpp - test_helpers.h -) -set_target_properties(test_utils PROPERTIES FOLDER tests) -target_link_libraries(test_utils PUBLIC shared) -target_include_directories(test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/libs/usvfs/test/test_utils/test_helpers.cpp b/libs/usvfs/test/test_utils/test_helpers.cpp deleted file mode 100644 index 2daf3bb..0000000 --- a/libs/usvfs/test/test_utils/test_helpers.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once - -#include <format> - -#include "test_helpers.h" -#include "winapi.h" - -namespace test -{ - -std::string FuncFailed::msg(std::string_view func, const char* arg1, - const unsigned long* res, const char* what) -{ - std::string buffer; - buffer.reserve(128); - - std::format_to(std::back_inserter(buffer), "{}() {}", func, what ? what : "failed"); - const char* sep = " : "; - if (arg1) { - std::format_to(std::back_inserter(buffer), "{}{}", sep, arg1); - sep = ", "; - } - if (res) { - std::format_to(std::back_inserter(buffer), "{}result = {} ({:#x})", sep, *res, - *res); - } - return buffer; -} - -ScopedFILE ScopedFILE::open(const std::filesystem::path& filepath, - std::wstring_view mode, errno_t& err) -{ - FILE* fp = nullptr; - err = _wfopen_s(&fp, filepath.c_str(), mode.data()); - if (err || !fp) { - return ScopedFILE(); - } - return ScopedFILE(fp); -} - -ScopedFILE ScopedFILE::open(const std::filesystem::path& filepath, - std::wstring_view mode) -{ - errno_t err; - auto file = open(filepath, mode, err); - if (err || !file) { - throw_testWinFuncFailed("_wfopen_s", filepath.string().c_str(), err); - } - return file; -} - -path path_of_test_bin(const path& relative) -{ - path base(winapi::wide::getModuleFileName(nullptr)); - return relative.empty() ? base.parent_path() : base.parent_path() / relative; -} - -path path_of_test_temp(const path& relative) -{ - return path_of_test_bin().parent_path() / "temp" / relative; -} - -path path_of_test_fixtures(const path& relative) -{ - return path_of_test_bin().parent_path() / "fixtures" / relative; -} - -path path_of_usvfs_lib(const path& relative) -{ - return path_of_test_bin().parent_path().parent_path() / "lib" / relative; -} - -std::string platform_dependant_executable(const char* name, const char* ext, - const char* platform) -{ - std::string res = name; - res += "_"; - if (platform) - res += platform; - else -#if _WIN64 - res += "x64"; -#else - res += "x86"; -#endif - res += "."; - res += ext; - return res; -} - -path path_as_relative(const path& base, const path& full_path) -{ - auto rel_begin = full_path.begin(); - auto base_iter = base.begin(); - while (rel_begin != full_path.end() && base_iter != base.end() && - *rel_begin == *base_iter) { - ++rel_begin; - ++base_iter; - } - - if (base_iter != base.end()) // full_path is not a sub-folder of base - return full_path; - - if (rel_begin == full_path.end()) // full_path == base - return path(L"."); - - // full_path is a sub-folder of base so take only relative path - path result; - for (; rel_begin != full_path.end(); ++rel_begin) - result /= *rel_begin; - return result; -} - -std::vector<char> read_small_file(const path& file, bool binary) -{ - using namespace std; - - const auto f = ScopedFILE::open(file, binary ? L"rb" : L"rt"); - - if (fseek(f, 0, SEEK_END)) - throw_testWinFuncFailed("fseek", (unsigned long)0); - - long size = ftell(f); - if (size < 0) - throw_testWinFuncFailed("ftell", (unsigned long)size); - if (size > 0x10000000) // sanity check limit to 256M - throw test::FuncFailed("read_small_file", "file size too large", - (unsigned long)size); - - if (fseek(f, 0, SEEK_SET)) - throw_testWinFuncFailed("fseek", (unsigned long)0); - - std::vector<char> content(static_cast<size_t>(size)); - content.resize(fread(content.data(), sizeof(char), content.size(), f)); - - return content; -} - -bool compare_files(const path& file1, const path& file2, bool binary) -{ - // TODO: if this is ever used for big file should read files in chunks - return read_small_file(file1, binary) == read_small_file(file2, binary); -} - -bool is_empty_folder(const path& dpath, bool or_doesnt_exist) -{ - bool isDir = false; - if (!winapi::ex::wide::fileExists(dpath.c_str(), &isDir)) - return or_doesnt_exist; - - if (!isDir) - return false; - - for (const auto& f : winapi::ex::wide::quickFindFiles(dpath.c_str(), L"*")) - if (f.fileName != L"." && f.fileName != L"..") - return false; - return true; -} - -void delete_file(const path& file) -{ - if (!DeleteFileW(file.c_str())) { - auto err = GetLastError(); - if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) - throw_testWinFuncFailed("DeleteFile", file.string()); - } -} - -void recursive_delete_files(const path& dpath) -{ - bool isDir = false; - if (!winapi::ex::wide::fileExists(dpath.c_str(), &isDir)) - return; - if (!isDir) - delete_file(dpath); - else { - // dpath exists and its a directory: - std::vector<std::wstring> recurse; - for (const auto& f : winapi::ex::wide::quickFindFiles(dpath.c_str(), L"*")) { - if (f.fileName == L"." || f.fileName == L"..") - continue; - if (f.attributes & FILE_ATTRIBUTE_DIRECTORY) - recurse.push_back(f.fileName); - else - delete_file(dpath / f.fileName); - } - for (auto r : recurse) - recursive_delete_files(dpath / r); - if (!RemoveDirectoryW(dpath.c_str())) - throw_testWinFuncFailed("RemoveDirectory", dpath.string().c_str()); - } - if (winapi::ex::wide::fileExists(dpath.c_str())) - throw FuncFailed("delete_directory_tree", dpath.string().c_str()); -} - -void recursive_copy_files(const path& src_path, const path& dest_path, bool overwrite) -{ - bool srcIsDir = false, destIsDir = false; - if (!winapi::ex::wide::fileExists(src_path.c_str(), &srcIsDir)) - throw FuncFailed("recursive_copy", "source doesn't exist", - src_path.string().c_str()); - if (!winapi::ex::wide::fileExists(dest_path.c_str(), &destIsDir) && srcIsDir) { - winapi::ex::wide::createPath(dest_path.c_str()); - destIsDir = true; - } - - if (!srcIsDir) - if (!destIsDir) { - if (!CopyFileW(src_path.c_str(), dest_path.c_str(), overwrite)) - throw_testWinFuncFailed( - "CopyFile", (src_path.string() + " => " + dest_path.string()).c_str()); - return; - } else - throw FuncFailed("recursive_copy", - "source is a file but destination is a directory", - (src_path.string() + ", " + dest_path.string()).c_str()); - - if (!destIsDir) - throw FuncFailed("recursive_copy", - "source is a directory but destination is a file", - (src_path.string() + ", " + dest_path.string()).c_str()); - - // source and destination are both directories: - std::vector<std::wstring> recurse; - for (const auto& f : winapi::ex::wide::quickFindFiles(src_path.c_str(), L"*")) { - if (f.fileName == L"." || f.fileName == L"..") - continue; - if (f.attributes & FILE_ATTRIBUTE_DIRECTORY) - recurse.push_back(f.fileName); - else if (!CopyFileW((src_path / f.fileName).c_str(), - (dest_path / f.fileName).c_str(), overwrite)) - throw_testWinFuncFailed("CopyFile", ((src_path / f.fileName).string() + " => " + - (dest_path / f.fileName).string())); - } - for (auto r : recurse) - recursive_copy_files(src_path / r, dest_path / r, overwrite); -} - -ScopedLoadLibrary::ScopedLoadLibrary(const wchar_t* dll_path) - : m_mod(LoadLibrary(dll_path)) -{} -ScopedLoadLibrary::~ScopedLoadLibrary() -{ - if (m_mod) - FreeLibrary(m_mod); -} - -}; // namespace test diff --git a/libs/usvfs/test/test_utils/test_helpers.h b/libs/usvfs/test/test_utils/test_helpers.h deleted file mode 100644 index 7ea8950..0000000 --- a/libs/usvfs/test/test_utils/test_helpers.h +++ /dev/null @@ -1,165 +0,0 @@ -#pragma once - -#include "windows_sane.h" - -#include <filesystem> -#include <format> -#include <memory> - -namespace test -{ - -class FuncFailed : public std::runtime_error -{ -public: - FuncFailed(const char* func) : std::runtime_error(msg(func)) {} - FuncFailed(const char* func, unsigned long res) - : std::runtime_error(msg(func, nullptr, &res)) - {} - FuncFailed(const char* func, const char* arg1) : std::runtime_error(msg(func, arg1)) - {} - FuncFailed(const char* func, const char* arg1, unsigned long res) - : std::runtime_error(msg(func, arg1, &res)) - {} - FuncFailed(const char* func, const char* what, const char* arg1) - : std::runtime_error(msg(func, arg1, nullptr, what)) - {} - FuncFailed(const char* func, const char* what, const char* arg1, unsigned long res) - : std::runtime_error(msg(func, arg1, &res, what)) - {} - -private: - std::string msg(std::string_view func, const char* arg1 = nullptr, - const unsigned long* res = nullptr, const char* what = nullptr); -}; - -class WinFuncFailed : public std::runtime_error -{ -public: - using runtime_error::runtime_error; -}; - -class WinFuncFailedGenerator -{ -public: - WinFuncFailedGenerator(DWORD gle = GetLastError()) : m_gle(gle) {} - WinFuncFailedGenerator(const WinFuncFailedGenerator&) = delete; - - DWORD lastError() const { return m_gle; } - - WinFuncFailed operator()(std::basic_string_view<char> func) - { - return WinFuncFailed(std::format("{} failed : lastError={}", func, m_gle)); - } - - WinFuncFailed operator()(std::basic_string_view<char> func, unsigned long res) - { - return WinFuncFailed( - std::format("{} failed : result=({:#x}), lastError={}", func, res, m_gle)); - } - - WinFuncFailed operator()(std::string_view func, std::basic_string_view<char> arg1) - { - return WinFuncFailed( - std::format("{} failed : {}, lastError={}", func, arg1, m_gle)); - } - - WinFuncFailed operator()(std::string_view func, std::basic_string_view<char> arg1, - unsigned long res) - { - return WinFuncFailed(std::format("{} failed : {}, result=({:#x}), lastError={}", - func, arg1, res, m_gle)); - } - -private: - DWORD m_gle; -}; - -// trick to guarantee the evalutation of GetLastError() before the evalution of the -// parameters to the WinFuncFailed message generation -template <class... Args> -[[noreturn]] void throw_testWinFuncFailed(std::string_view func, Args&&... args) -{ - ::test::WinFuncFailedGenerator exceptionGenerator; - throw exceptionGenerator(func, std::forward<Args>(args)...); -} - -class ScopedFILE -{ -public: - // try to open the given filepath with the given mode, if it fails, set err to - // the return code of _wfopen_s and return a nulled scoped file - // - static ScopedFILE open(const std::filesystem::path& filepath, std::wstring_view mode, - errno_t& err); - - // same as above but throw a WinFuncFailed() exception if opening the file failed - // - static ScopedFILE open(const std::filesystem::path& filepath, std::wstring_view mode); - -public: - ScopedFILE(FILE* f = nullptr) : m_f(f, &fclose) {} - - ScopedFILE(ScopedFILE&& other) noexcept = default; - ~ScopedFILE() = default; - - ScopedFILE(const ScopedFILE&) = delete; - - void close() { m_f = nullptr; } - - operator bool() const { return static_cast<bool>(m_f); } - operator FILE*() const { return m_f.get(); } - -private: - std::unique_ptr<FILE, decltype(&fclose)> m_f; -}; - -using std::filesystem::path; - -// path functions assume they are called by a test executable -// (calculate the requested path relative to the current executable path) - -path path_of_test_bin(const path& relative = path()); -path path_of_test_temp(const path& relative = path()); -path path_of_test_fixtures(const path& relative = path()); -path path_of_usvfs_lib(const path& relative = path()); - -std::string platform_dependant_executable(const char* name, const char* ext = "exe", - const char* platform = nullptr); - -// if full_path is a subfolder of base returns only the relative path, -// if full_path and base are the same folder "." is returned, -// otherwise full_path is returned unchanged -path path_as_relative(const path& base, const path& full_path); - -std::vector<char> read_small_file(const path& file, bool binary = true); - -// true iff the the contents of the two files is exactly the same -bool compare_files(const path& file1, const path& file2, bool binary = true); - -// return true iff the given path is an empty (optionally true also if path doesn't -// exist) -bool is_empty_folder(const path& dpath, bool or_doesnt_exist = false); - -void delete_file(const path& file); - -// Recursively deletes the given path and all the files and directories under it -// Use with care!!! -void recursive_delete_files(const path& dpath); - -// Recursively copies all files and directories from srcPath to destPath -void recursive_copy_files(const path& src_path, const path& dest_path, bool overwrite); - -class ScopedLoadLibrary -{ -public: - ScopedLoadLibrary(const wchar_t* dll_path); - ~ScopedLoadLibrary(); - - // returns zero if load library failed - operator HMODULE() const { return m_mod; } - -private: - HMODULE m_mod; -}; -}; // namespace test diff --git a/libs/usvfs/test/thooklib_test/CMakeLists.txt b/libs/usvfs/test/thooklib_test/CMakeLists.txt deleted file mode 100644 index cca9e10..0000000 --- a/libs/usvfs/test/thooklib_test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) -find_package(Boost CONFIG REQUIRED COMPONENTS thread) - -# not sure why there is a test_hooks.cpp here? -add_executable(thooklib_test main.cpp) -usvfs_set_test_properties(thooklib_test) -target_link_libraries(thooklib_test - PRIVATE test_utils shared thooklib Boost::thread - GTest::gtest GTest::gtest_main) diff --git a/libs/usvfs/test/thooklib_test/main.cpp b/libs/usvfs/test/thooklib_test/main.cpp deleted file mode 100644 index df48198..0000000 --- a/libs/usvfs/test/thooklib_test/main.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include <gtest/gtest.h> -#include <hooklib.h> -#include <iostream> -#include <ttrampolinepool.h> -#include <utility.h> -#include <windows_sane.h> -// #include <boost/thread.hpp> -#include <boost/filesystem.hpp> -#include <exceptionex.h> -#include <spdlog/sinks/stdout_sinks.h> -#include <spdlog/spdlog.h> -#include <winapi.h> - -namespace fs = boost::filesystem; - -#include <stringutils.h> - -using namespace std; -using namespace HookLib; - -class TempFile -{ -public: - TempFile(const wchar_t* relative) : wpath(winapi::wide::getModuleFileName(nullptr)) - { - size_t path_end = wpath.rfind(L'\\'); - if (path_end != std::wstring::npos) { - wpath.erase(path_end + 1); - wpath += L"..\\temp\\"; - wpath += relative; - } else - wpath = relative; - path = - usvfs::shared::string_cast<std::string>(wpath, usvfs::shared::CodePage::UTF8); - } - - const char* c_str() const { return path.c_str(); } - const wchar_t* w_str() const { return wpath.c_str(); } - -private: - std::string path; - std::wstring wpath; -}; - -static const HANDLE MARKERHANDLE = reinterpret_cast<HANDLE>(0x1CC0FFEE); -static const TempFile VALID_FILENAME{L"VALID_FILENAME"}; -static const TempFile INVALID_FILENAME{L"\\<>/"}; - -#include "test_hooks.cpp" - -static bool stubCalled = false; - -void __cdecl CreateFileStub(LPVOID) -{ - stubCalled = true; -} - -class HookingTest : public testing::Test -{ -public: - void SetUp() - { - /* typedef sinks::synchronous_sink<sinks::text_ostream_backend> text_sink; - boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>(); - - // Add a stream to write log to - sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>("c:\\temp\\testing_out.log")); - - // Register the sink in the logging core - logging::core::get()->add_sink(sink); - - sink->set_filter(expr::attr<LogLevel>("Severity") >= LogLevel::Debug);*/ - } - - void TearDown() {} - -private: -}; - -TEST(GetProcAddressTest, ReturnsValidResults) -{ - HMODULE mh = GetModuleHandleA("KernelBase.dll"); - EXPECT_NE(nullptr, mh); - EXPECT_EQ(GetProcAddress(mh, "CreateFileA"), MyGetProcAddress(mh, "CreateFileA")); -} - -TEST_F(HookingTest, CanHook) -{ - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileW", THCreateFileW_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileW", THCreateFileW_1); - } - EXPECT_NE(INVALID_HOOK, hook); - RemoveHook(hook); -} - -TEST_F(HookingTest, CanStub) -{ - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallStub(k32Mod, "CreateFileW", CreateFileStub); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallStub(k32Mod, "CreateFileW", CreateFileStub); - } - EXPECT_NE(INVALID_HOOK, hook); - RemoveHook(hook); -} - -TEST_F(HookingTest, RemoveHook) -{ - // test that we can remove a hook - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - } - - EXPECT_NE(INVALID_HOOK, hook); - RemoveHook(hook); - HANDLE test = CreateFileA(INVALID_FILENAME.c_str(), GENERIC_READ, 0, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - EXPECT_EQ(INVALID_HANDLE_VALUE, test); -} - -TEST_F(HookingTest, CreateFileStubTest) -{ - stubCalled = false; - // test if our stub works - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallStub(k32Mod, "CreateFileA", CreateFileStub); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallStub(k32Mod, "CreateFileA", CreateFileStub); - } - EXPECT_NE(INVALID_HOOK, hook); - HANDLE test = CreateFileA(INVALID_FILENAME.c_str(), GENERIC_READ, 0, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - RemoveHook(hook); - EXPECT_EQ(true, stubCalled); - EXPECT_EQ(INVALID_HANDLE_VALUE, test); -} - -TEST_F(HookingTest, CreateFileHook) -{ - // test if our hook works - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - } - HANDLE test = CreateFileA(INVALID_FILENAME.c_str(), GENERIC_READ, 0, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - RemoveHook(hook); - EXPECT_EQ(MARKERHANDLE, test); -} - -TEST_F(HookingTest, CreateFileWHook) -{ - // test if our hook works - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileW", THCreateFileW_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileW", THCreateFileW_1); - } - HANDLE test = CreateFileW(INVALID_FILENAME.w_str(), 0x42, 0x43, - (LPSECURITY_ATTRIBUTES)0x44, 0x45, 0x46, (HANDLE)0x47); - RemoveHook(hook); - EXPECT_EQ(MARKERHANDLE, test); -} - -TEST_F(HookingTest, CreateFileHookRecursion) -{ - // test that the trampoline works, so we can call the original function from - // within the hook - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - } - HANDLE test = CreateFileA(VALID_FILENAME.c_str(), GENERIC_READ, 0, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - RemoveHook(hook); - EXPECT_NE(MARKERHANDLE, test); -} - -TEST_F(HookingTest, Threading) -{ - // test that multiple threads can concurrently call a hooked function without - // incorrect results. - // TODO: this test doesn't reliably find thread-unsafeties - // NOTE: the hooklib currently does not claim that hook installation or removal - // is thread-safe, only the hooked functions shouldn't become less thread-safe by - // being hooked! - - static const int NUM_THREADS = 100; - static const int NUM_TRIES = 1000; - - HMODULE k32Mod = GetModuleHandleA("kernel32.dll"); - HOOKHANDLE hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - if (hook == INVALID_HOOK) { - k32Mod = GetModuleHandleA("kernelbase.dll"); - hook = InstallHook(k32Mod, "CreateFileA", THCreateFileA_1); - } - std::thread threads[NUM_THREADS]; - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i] = std::thread([i] { - for (int count = 0; count < NUM_TRIES; ++count) { - HANDLE test = CreateFileA(INVALID_FILENAME.c_str(), GENERIC_READ, 0, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - EXPECT_EQ(MARKERHANDLE, test); - } - }); - } - - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i].join(); - } - - RemoveHook(hook); -} - -int main(int argc, char** argv) -{ - auto logger = spdlog::stdout_logger_mt("usvfs"); - logger->set_level(spdlog::level::warn); - TrampolinePool::initialize(); - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/thooklib_test/test_hooks.cpp b/libs/usvfs/test/thooklib_test/test_hooks.cpp deleted file mode 100644 index edde2c8..0000000 --- a/libs/usvfs/test/thooklib_test/test_hooks.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include <Windows.h> -#include <gtest/gtest.h> - -HANDLE WINAPI THCreateFileA_1(LPCSTR lpFileName, DWORD dwDesiredAccess, - DWORD dwShareMode, - LPSECURITY_ATTRIBUTES lpSecurityAttributes, - DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, - HANDLE hTemplateFile) -{ - if (strcmp(lpFileName, INVALID_FILENAME.c_str()) == 0) { - return MARKERHANDLE; - } else { - HANDLE res = - ::CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, - dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - return res; - } -} - -HANDLE WINAPI THCreateFileW_1(LPCWSTR lpFileName, DWORD dwDesiredAccess, - DWORD dwShareMode, - LPSECURITY_ATTRIBUTES lpSecurityAttributes, - DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, - HANDLE hTemplateFile) -{ - if (wcscmp(lpFileName, INVALID_FILENAME.w_str()) == 0) { - EXPECT_EQ(0x42, dwDesiredAccess); - EXPECT_EQ(0x43, dwShareMode); - EXPECT_EQ(0x44, (int)lpSecurityAttributes); - EXPECT_EQ(0x45, dwCreationDisposition); - EXPECT_EQ(0x46, dwFlagsAndAttributes); - EXPECT_EQ(0x47, (int)hTemplateFile); - return MARKERHANDLE; - } else { - return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, - dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - } -} diff --git a/libs/usvfs/test/tinjectlib_test/CMakeLists.txt b/libs/usvfs/test/tinjectlib_test/CMakeLists.txt deleted file mode 100644 index 9bd3f7b..0000000 --- a/libs/usvfs/test/tinjectlib_test/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -# binary and binary injected -add_executable(testinject_bin testinject_bin/main.cpp) -usvfs_set_test_properties(testinject_bin FOLDER tinjectlib) - -add_library(testinject_dll SHARED testinject_dll/main.cpp testinject_dll/main.h) -usvfs_set_test_properties(testinject_dll FOLDER tinjectlib) - -# actual test executable -add_executable(tinjectlib_test main.cpp) -usvfs_set_test_properties(tinjectlib_test FOLDER tinjectlib) -target_link_libraries(tinjectlib_test PRIVATE tinjectlib shared GTest::gtest GTest::gtest_main) -add_dependencies(tinjectlib_test testinject_bin testinject_dll) diff --git a/libs/usvfs/test/tinjectlib_test/main.cpp b/libs/usvfs/test/tinjectlib_test/main.cpp deleted file mode 100644 index ce48f83..0000000 --- a/libs/usvfs/test/tinjectlib_test/main.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include <boost/filesystem.hpp> -#include <boost/type_traits.hpp> -#include <gtest/gtest.h> -#include <injectlib.h> -#include <spdlog/sinks/stdout_sinks.h> -#include <spdlog/spdlog.h> -#include <winapi.h> - -using namespace usvfs::shared; -using namespace InjectLib; - -#if BOOST_ARCH_X86_64 -static const wchar_t INJECT_BIN[] = L"testinject_bin_x64.exe"; -#else -static const wchar_t INJECT_BIN[] = L"testinject_bin_x86.exe"; -#endif - -#if BOOST_ARCH_X86_64 -static const wchar_t INJECT_LIB[] = L"testinject_dll_x64.dll"; -#else -static const wchar_t INJECT_LIB[] = L"testinject_dll_x86.dll"; -#endif - -static std::shared_ptr<spdlog::logger> logger() -{ - std::shared_ptr<spdlog::logger> result = spdlog::get("test"); - if (result.get() == nullptr) { - result = spdlog::stdout_logger_mt("test"); - } - return result; -} - -bool spawn(HANDLE& processHandle, HANDLE& threadHandle) -{ - STARTUPINFO si; - ::ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - PROCESS_INFORMATION pi; - BOOL success = ::CreateProcess(INJECT_BIN, nullptr, nullptr, nullptr, FALSE, - CREATE_SUSPENDED, nullptr, nullptr, &si, &pi); - - if (!success) { - throw windows_error("failed to start process"); - } - - processHandle = pi.hProcess; - threadHandle = pi.hThread; - - return true; -} - -TEST(InjectingTest, InjectionNoInit) -{ - // Verify lib can inject without a init function - - HANDLE process, thread; - spawn(process, thread); - EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB)); - ResumeThread(thread); - - DWORD res = WaitForSingleObject(process, INFINITE); - DWORD exitCode = NO_ERROR; - res = GetExitCodeProcess(process, &exitCode); - EXPECT_EQ(NOERROR, exitCode); - - CloseHandle(process); - CloseHandle(thread); -} - -TEST(InjectingTest, InjectionSimpleInit) -{ - // Verify lib can inject with a init function with null parameters - - HANDLE process, thread; - spawn(process, thread); - EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitNoParam")); - ResumeThread(thread); - - DWORD res = WaitForSingleObject(process, INFINITE); - DWORD exitCode = NO_ERROR; - res = GetExitCodeProcess(process, &exitCode); - EXPECT_EQ(10001, exitCode); // used init function exits process with this exit code - - CloseHandle(process); - CloseHandle(thread); -} - -TEST(InjectingTest, InjectionComplexInit) -{ - // Verify lib can inject with a init function with null parameters - - static const WCHAR param[] = L"magic_parameter"; - HANDLE process, thread; - spawn(process, thread); - EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitComplexParam", - reinterpret_cast<LPCVOID>(param), - wcslen(param) * sizeof(WCHAR))); - - ResumeThread(thread); - - DWORD res = WaitForSingleObject(process, INFINITE); - DWORD exitCode = NO_ERROR; - res = GetExitCodeProcess(process, &exitCode); - EXPECT_EQ(10002, exitCode); // used init function exits process with this exit code - - CloseHandle(process); - CloseHandle(thread); -} - -TEST(InjectingTest, InjectionNoQuitInit) -{ - // Verify lib can inject with a init function with null parameters - - HANDLE process, thread; - spawn(process, thread); - EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitNoQuit")); - ResumeThread(thread); - - DWORD res = WaitForSingleObject(process, INFINITE); - DWORD exitCode = NO_ERROR; - res = GetExitCodeProcess(process, &exitCode); - EXPECT_EQ(0, exitCode); // expect regular exit from process - - CloseHandle(process); - CloseHandle(thread); -} - -TEST(InjectingTest, InjectionSkipInit) -{ - // verify the skip-on-missing mechanism for init function works - - HANDLE process, thread; - spawn(process, thread); - EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "__InitInvalid", - nullptr, 0, true)); - ResumeThread(thread); - - DWORD res = WaitForSingleObject(process, INFINITE); - DWORD exitCode = NO_ERROR; - res = GetExitCodeProcess(process, &exitCode); - EXPECT_EQ(NOERROR, exitCode); - - CloseHandle(process); - CloseHandle(thread); -} - -int main(int argc, char** argv) -{ - auto logger = spdlog::stdout_logger_mt("usvfs"); - logger->set_level(spdlog::level::warn); - - boost::filesystem::path filePath(winapi::wide::getModuleFileName(nullptr)); - SetCurrentDirectoryW(filePath.parent_path().wstring().c_str()); - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/tinjectlib_test/testinject_bin/main.cpp b/libs/usvfs/test/tinjectlib_test/testinject_bin/main.cpp deleted file mode 100644 index f8b643a..0000000 --- a/libs/usvfs/test/tinjectlib_test/testinject_bin/main.cpp +++ /dev/null @@ -1,4 +0,0 @@ -int main() -{ - return 0; -} diff --git a/libs/usvfs/test/tinjectlib_test/testinject_dll/main.cpp b/libs/usvfs/test/tinjectlib_test/testinject_dll/main.cpp deleted file mode 100644 index 8339e97..0000000 --- a/libs/usvfs/test/tinjectlib_test/testinject_dll/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "main.h" - -void __cdecl InitNoQuit(LPVOID, size_t) -{ - // nop -} - -void __cdecl InitNoParam(LPVOID, size_t) -{ - ExitProcess(10001); -} - -void __cdecl InitComplexParam(LPVOID userData, size_t) -{ - LPCWSTR string = (LPCWSTR)userData; - if (wcscmp(string, L"magic_parameter") == 0) { - ExitProcess(10002); - } else { - ExitProcess(20003); - } -} - -BOOL APIENTRY DllMain(HMODULE, DWORD reasonForCall, LPVOID) -{ - switch (reasonForCall) { - case DLL_PROCESS_ATTACH: { - } break; - case DLL_PROCESS_DETACH: { - } break; - case DLL_THREAD_ATTACH: { - } break; - case DLL_THREAD_DETACH: { - } break; - } - return TRUE; -} diff --git a/libs/usvfs/test/tinjectlib_test/testinject_dll/main.h b/libs/usvfs/test/tinjectlib_test/testinject_dll/main.h deleted file mode 100644 index 6faf800..0000000 --- a/libs/usvfs/test/tinjectlib_test/testinject_dll/main.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -extern "C" - __declspec(dllexport) void __cdecl InitNoQuit(LPVOID userData, size_t userDataSize); -extern "C" __declspec(dllexport) void __cdecl InitNoParam(LPVOID userData, - size_t userDataSize); -extern "C" __declspec(dllexport) void __cdecl InitComplexParam(LPVOID userData, - size_t userDataSize); diff --git a/libs/usvfs/test/tvfs_test/CMakeLists.txt b/libs/usvfs/test/tvfs_test/CMakeLists.txt deleted file mode 100644 index f66a7b4..0000000 --- a/libs/usvfs/test/tvfs_test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -add_executable(tvfs_test main.cpp) -usvfs_set_test_properties(tvfs_test) -target_link_libraries(tvfs_test PRIVATE test_utils usvfs_helper GTest::gtest GTest::gmock GTest::gtest_main) -usvfs_target_link_usvfs(tvfs_test) - -# tvfs_test uses a private USVFS header so we need to include it manually -get_target_property(USVFS_SOURCE_DIR usvfs_dll SOURCE_DIR) -target_include_directories(tvfs_test PRIVATE ${USVFS_SOURCE_DIR}) diff --git a/libs/usvfs/test/tvfs_test/main.cpp b/libs/usvfs/test/tvfs_test/main.cpp deleted file mode 100644 index 7c7998b..0000000 --- a/libs/usvfs/test/tvfs_test/main.cpp +++ /dev/null @@ -1,585 +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/>. -*/ - -// this file depends on so many stuff that the easiest way is to include -// pch.h from shared -#include "pch.h" - -#include <test_helpers.h> - -#include <fstream> -#include <iostream> - -#include <inject.h> -#include <stringutils.h> -#include <windows_sane.h> - -#include <spdlog/sinks/stdout_sinks.h> -#include <spdlog/spdlog.h> - -#include <hookcontext.h> -#include <hooks/kernel32.h> -#include <hooks/ntdll.h> -#include <logging.h> -#include <stringcast.h> -#include <unicodestring.h> -#include <usvfs.h> - -#include <gmock/gmock-matchers.h> -#include <gtest/gtest.h> - -namespace spd = spdlog; - -namespace ush = usvfs::shared; - -// name of a file to be created in the virtual fs. Shouldn't exist on disc but the -// directory must exist -static LPCSTR VIRTUAL_FILEA = "C:/np.exe"; -static LPCWSTR VIRTUAL_FILEW = L"C:/np.exe"; - -// a real file on disc that has to exist -static LPCSTR REAL_FILEA = "C:/windows/notepad.exe"; -static LPCWSTR REAL_FILEW = L"C:/windows/notepad.exe"; - -static LPCSTR REAL_DIRA = "C:/windows/Logs"; -static LPCWSTR REAL_DIRW = L"C:/windows/Logs"; - -static std::shared_ptr<spdlog::logger> logger() -{ - std::shared_ptr<spdlog::logger> result = spdlog::get("test"); - if (result.get() == nullptr) { - result = spdlog::stdout_logger_mt("test"); - } - return result; -} - -auto defaultUsvfsParams(const char* instanceName = "usvfs_test") -{ - std::unique_ptr<usvfsParameters, decltype(&usvfsFreeParameters)> parameters{ - usvfsCreateParameters(), &usvfsFreeParameters}; - - usvfsSetInstanceName(parameters.get(), instanceName); - usvfsSetDebugMode(parameters.get(), true); - usvfsSetLogLevel(parameters.get(), LogLevel::Debug); - usvfsSetCrashDumpType(parameters.get(), CrashDumpsType::None); - usvfsSetCrashDumpPath(parameters.get(), ""); - - return std::move(parameters); -} - -class USVFSTest : public testing::Test -{ -public: - void SetUp() - { - SHMLogger::create("usvfs"); - // need to initialize logging in the context of the dll - usvfsInitLogging(); - } - - void TearDown() - { - std::array<char, 1024> buffer; - while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { - std::cout << buffer.data() << std::endl; - } - SHMLogger::free(); - } - -private: -}; - -class USVFSTestWithReroute : public testing::Test -{ -public: - void SetUp() - { - SHMLogger::create("usvfs"); - // need to initialize logging in the context of the dll - usvfsInitLogging(); - - auto params = defaultUsvfsParams(); - m_Context.reset(usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer& tree = m_Context->redirectionTable(); - tree.addFile( - ush::string_cast<std::string>(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), - usvfs::RedirectionDataLocal(REAL_FILEA)); - } - - void TearDown() - { - std::array<char, 1024> buffer; - while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { - std::cout << buffer.data() << std::endl; - } - m_Context.reset(); - SHMLogger::free(); - } - -private: - std::unique_ptr<usvfs::HookContext> m_Context; -}; - -class USVFSTestAuto : public testing::Test -{ -public: - void SetUp() - { - auto params = defaultUsvfsParams(); - usvfsConnectVFS(params.get()); - SHMLogger::create("usvfs"); - } - - void TearDown() - { - usvfsDisconnectVFS(); - - std::array<char, 1024> buffer; - while (SHMLogger::instance().tryGet(buffer.data(), buffer.size())) { - std::cout << buffer.data() << std::endl; - } - SHMLogger::free(); - } - -private: -}; - -TEST_F(USVFSTest, CanResizeRedirectiontree) -{ - using usvfs::shared::MissingThrow; - ASSERT_NO_THROW({ - usvfs::RedirectionTreeContainer container("treetest_shm", 1024); - for (char i = 'a'; i <= 'z'; ++i) { - for (char j = 'a'; j <= 'z'; ++j) { - std::string name = std::string(R"(C:\temp\)") + i + j; - container.addFile(name, usvfs::RedirectionDataLocal("gaga"), false); - } - } - - ASSERT_EQ("gaga", container->node("C:") - ->node("temp") - ->node("aa", MissingThrow) - ->data() - .linkTarget); - ASSERT_EQ("gaga", container->node("C:") - ->node("temp") - ->node("az", MissingThrow) - ->data() - .linkTarget); - }); -} - -/* -TEST_F(USVFSTest, CreateFileHookReportsCorrectErrorOnMissingFile) -{ - ASSERT_NO_THROW({ - USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, -CrashDumpsType::None, ""); std::unique_ptr<usvfs::HookContext> -ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); HANDLE res = -usvfs::hook_CreateFileW(VIRTUAL_FILEW , GENERIC_READ , FILE_SHARE_READ | -FILE_SHARE_WRITE , nullptr , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , nullptr); - - ASSERT_EQ(INVALID_HANDLE_VALUE, res); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); - }); -} -*/ - -/* -TEST_F(USVFSTestWithReroute, CreateFileHookRedirectsFile) -{ - ASSERT_NE(INVALID_HANDLE_VALUE - , usvfs::hook_CreateFileW(VIRTUAL_FILEW - , GENERIC_READ - , FILE_SHARE_READ | FILE_SHARE_WRITE - , nullptr - , OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL - , nullptr)); -} -*/ - -TEST_F(USVFSTest, GetFileAttributesHookReportsCorrectErrorOnMissingFile) -{ - ASSERT_NO_THROW({ - try { - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - DWORD res = usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW); - - ASSERT_EQ(INVALID_FILE_ATTRIBUTES, res); - ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); - } catch (const std::exception& e) { - logger()->error("Exception: {}", e.what()); - throw; - } - }); -} - -TEST_F(USVFSTest, GetFileAttributesHookRedirectsFile) -{ - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - - tree.addFile( - ush::string_cast<std::string>(VIRTUAL_FILEW, ush::CodePage::UTF8).c_str(), - usvfs::RedirectionDataLocal(REAL_FILEA)); - - ASSERT_EQ(::GetFileAttributesW(REAL_FILEW), - usvfs::hook_GetFileAttributesW(VIRTUAL_FILEW)); -} -/* -TEST_F(USVFSTest, GetFullPathNameOnRegularCurrentDirectory) -{ - USVFSParameters params; - USVFSInitParameters(¶ms, "usvfs_test", true, LogLevel::Debug, -CrashDumpsType::None, ""); std::unique_ptr<usvfs::HookContext> -ctx(CreateHookContext(params, ::GetModuleHandle(nullptr))); - - std::wstring expected = winapi::wide::getCurrentDirectory() + L"\\filename.txt"; - - DWORD bufferLength = 32767; - std::unique_ptr<wchar_t[]> buffer(new wchar_t[bufferLength]); - LPWSTR filePart = nullptr; - - DWORD res = usvfs::hook_GetFullPathNameW(L"filename.txt", bufferLength, buffer.get(), -&filePart); - - ASSERT_NE(0UL, res); - ASSERT_EQ(expected, std::wstring(buffer.get())); -}*/ - -// small wrapper to call usvfs::hook_NtOpenFile with a path -// -// at some point in time, changes were made to USVFS such that calling a hooked -// function from a handle obtained from a non-hooked function would not work anymore, -// meaning that function such as CreateFileW that have no hook equivalent cannot -// be used to test hook functions -// -// this function is useful to simulate a CreateFileW by internally using the hook -// version of NtOpenFile -// -HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess, - ULONG openOptions) -{ - constexpr size_t BUFFER_SIZE = 2048; - IO_STATUS_BLOCK statusBlock; - OBJECT_ATTRIBUTES attributes; - attributes.SecurityDescriptor = 0; - attributes.SecurityQualityOfService = 0; - attributes.RootDirectory = 0; - attributes.Attributes = 0; - attributes.Length = sizeof(OBJECT_ATTRIBUTES); - - WCHAR stringBuffer[BUFFER_SIZE]; - UNICODE_STRING string; - string.Buffer = stringBuffer; - lstrcpyW(stringBuffer, L"\\??\\"); - lstrcatW(stringBuffer, path); - string.Length = static_cast<USHORT>(lstrlenW(stringBuffer) * 2); - string.MaximumLength = BUFFER_SIZE; - attributes.ObjectName = &string; - - HANDLE ret = INVALID_HANDLE_VALUE; - if (usvfs::hook_NtOpenFile(&ret, accessMask, &attributes, &statusBlock, shareAccess, - openOptions) != STATUS_SUCCESS) { - return INVALID_HANDLE_VALUE; - } - - return ret; -} - -TEST_F(USVFSTest, NtQueryDirectoryFileRegularFile) -{ - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - - HANDLE hdl = - hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); - ASSERT_NE(INVALID_HANDLE_VALUE, hdl); - - IO_STATUS_BLOCK status; - char buffer[1024]; - - usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, - 1024, FileDirectoryInformation, TRUE, nullptr, TRUE); - - ASSERT_EQ(STATUS_SUCCESS, status.Status); - - usvfs::hook_NtClose(hdl); -} - -TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile) -{ - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - - tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); - - HANDLE hdl = - hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); - ASSERT_NE(INVALID_HANDLE_VALUE, hdl); - - IO_STATUS_BLOCK status; - char buffer[1024]; - - usvfs::UnicodeString fileName(L"np.exe"); - - usvfs::hook_NtQueryDirectoryFile(hdl, nullptr, nullptr, nullptr, &status, buffer, - 1024, FileDirectoryInformation, TRUE, - static_cast<PUNICODE_STRING>(fileName), TRUE); - - FILE_DIRECTORY_INFORMATION* info = - reinterpret_cast<FILE_DIRECTORY_INFORMATION*>(buffer); - ASSERT_EQ(STATUS_SUCCESS, status.Status); - ASSERT_EQ(0, wcscmp(info->FileName, L"np.exe")); - - usvfs::hook_NtClose(hdl); -} - -TEST_F(USVFSTest, NtQueryDirectoryFileExVirtualFile) -{ - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - - tree.addFile(L"C:\\0123456789.txt", usvfs::RedirectionDataLocal(REAL_FILEA)); - tree.addFile(L"C:\\123456", usvfs::RedirectionDataLocal(REAL_FILEA)); - tree.addFile(L"C:\\abcdef", usvfs::RedirectionDataLocal(REAL_FILEA)); - tree.addFile(L"C:\\abcdefghijklmnopqrstuvwxyz.txt", - usvfs::RedirectionDataLocal(REAL_FILEA)); - - HANDLE hdl = - hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); - ASSERT_NE(INVALID_HANDLE_VALUE, hdl); - - IO_STATUS_BLOCK status; - - constexpr size_t BUFFER_SIZE = 2048; - char buffer[BUFFER_SIZE]; - - std::vector<std::wstring> foundFiles; - while (usvfs::hook_NtQueryDirectoryFileEx( - hdl, nullptr, nullptr, nullptr, &status, buffer, BUFFER_SIZE, - FileFullDirectoryInformation, 0, nullptr) == STATUS_SUCCESS) { - std::size_t offset = 0; - while (offset < BUFFER_SIZE) { - const auto* info = reinterpret_cast<FILE_FULL_DIR_INFORMATION*>(buffer + offset); - foundFiles.emplace_back(info->FileName, info->FileNameLength / sizeof(wchar_t)); - - if (info->NextEntryOffset == 0) { - break; // no more entries - } - - offset += info->NextEntryOffset; - } - } - - ASSERT_THAT(foundFiles, - ::testing::IsSupersetOf({L"0123456789.txt", L"123456", L"abcdef", - L"abcdefghijklmnopqrstuvwxyz.txt"})); - - usvfs::hook_NtClose(hdl); -} - -TEST_F(USVFSTest, NtQueryObjectVirtualFile) -{ - std::wstring c_drive_device; - { - // find the device path for C: - HANDLE hdl = ::CreateFileW( - L"C:\\", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, hdl); - - char buffer[2048]; - ASSERT_EQ(STATUS_SUCCESS, ::NtQueryObject(hdl, ObjectNameInformation, buffer, - sizeof(buffer), nullptr)); - - OBJECT_NAME_INFORMATION* information = - reinterpret_cast<OBJECT_NAME_INFORMATION*>(buffer); - - c_drive_device = - std::wstring(information->Name.Buffer, information->Name.Length / 2); - - ::CloseHandle(hdl); - } - - auto params = defaultUsvfsParams(); - std::unique_ptr<usvfs::HookContext> ctx( - usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr))); - usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable(); - - tree.addFile(L"C:\\np.exe", usvfs::RedirectionDataLocal(REAL_FILEA)); - - HANDLE hdl = hooked_NtOpenFile(L"C:\\np.exe", FILE_GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); - ASSERT_NE(INVALID_HANDLE_VALUE, hdl) << "last error=" << ::GetLastError(); - - { - char buffer[1024]; - IO_STATUS_BLOCK status; - const auto res = usvfs::hook_NtQueryInformationFile( - hdl, &status, buffer, sizeof(buffer), FileNameInformation); - ASSERT_EQ(STATUS_SUCCESS, res); - ASSERT_EQ(STATUS_SUCCESS, status.Status); - - FILE_NAME_INFORMATION* fileNameInfo = - reinterpret_cast<FILE_NAME_INFORMATION*>(buffer); - ASSERT_EQ(L"\\np.exe", - std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); - } - - { - char buffer[1024]; - IO_STATUS_BLOCK status; - const auto res = usvfs::hook_NtQueryInformationFile( - hdl, &status, buffer, sizeof(buffer), FileNormalizedNameInformation); - ASSERT_EQ(STATUS_SUCCESS, res); - ASSERT_EQ(STATUS_SUCCESS, status.Status); - ASSERT_EQ(sizeof(ULONG) + 7 * 2, status.Information); - - FILE_NAME_INFORMATION* fileNameInfo = - reinterpret_cast<FILE_NAME_INFORMATION*>(buffer); - ASSERT_EQ(L"\\np.exe", - std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); - } - - // buffer of size should be too small for the original path (\Windows\notepad.exe) - // but not for \np.exe - { - // the required size should be sizeof(ULONG) + 7 * 2 but apparently that is - // not enough for the CI so using 16 * 2 which should be large enough for - // the hooked version, but still too short for the non-hooked one - char buffer[sizeof(ULONG) + 7 * 2]; - IO_STATUS_BLOCK status; - NTSTATUS res; - - res = ::NtQueryInformationFile(hdl, &status, buffer, sizeof(buffer), - FileNameInformation); - ASSERT_EQ(STATUS_BUFFER_OVERFLOW, res); - ASSERT_EQ(STATUS_BUFFER_OVERFLOW, status.Status); - - res = usvfs::hook_NtQueryInformationFile(hdl, &status, buffer, sizeof(buffer), - FileNameInformation); - ASSERT_EQ(STATUS_SUCCESS, res); - ASSERT_EQ(STATUS_SUCCESS, status.Status); - ASSERT_EQ(sizeof(ULONG) + 7 * 2, status.Information); - - FILE_NAME_INFORMATION* fileNameInfo = - reinterpret_cast<FILE_NAME_INFORMATION*>(buffer); - ASSERT_EQ(L"\\np.exe", - std::wstring(fileNameInfo->FileName, fileNameInfo->FileNameLength / 2)); - } - - { - char buffer[2048]; - const auto res = usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, - sizeof(buffer), nullptr); - ASSERT_EQ(STATUS_SUCCESS, res); - - OBJECT_NAME_INFORMATION* information = - reinterpret_cast<OBJECT_NAME_INFORMATION*>(buffer); - ASSERT_EQ(c_drive_device + L"np.exe", - std::wstring(information->Name.Buffer, - information->Name.Length / sizeof(wchar_t))); - } - - { - // expected length is sizeof struct + size of path (in bytes), including the - // null-character - const auto expectedLength = - sizeof(OBJECT_NAME_INFORMATION) + c_drive_device.size() * 2 + 12 + 2; - ULONG requiredLength; - NTSTATUS res; - char buffer[2048]; - - res = - usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, - sizeof(OBJECT_NAME_INFORMATION) - 1, &requiredLength); - ASSERT_EQ(STATUS_INFO_LENGTH_MISMATCH, res); - ASSERT_EQ(expectedLength, requiredLength); - - res = usvfs::hook_NtQueryObject(hdl, ObjectNameInformation, buffer, - sizeof(OBJECT_NAME_INFORMATION), &requiredLength); - ASSERT_EQ(STATUS_BUFFER_OVERFLOW, res); - ASSERT_EQ(expectedLength, requiredLength); - } - - usvfs::hook_NtClose(hdl); -} - -TEST_F(USVFSTestAuto, CannotCreateLinkToFileInNonexistantDirectory) -{ - ASSERT_EQ(FALSE, usvfsVirtualLinkFile( - REAL_FILEW, L"c:/this_directory_shouldnt_exist/np.exe", FALSE)); -} - -TEST_F(USVFSTestAuto, CanCreateMultipleLinks) -{ - static LPCWSTR outFile = LR"(C:\np.exe)"; - static LPCWSTR outDir = LR"(C:\logs)"; - static LPCWSTR outDirCanonizeTest = LR"(C:\.\not/../logs\.\a\.\b\.\c\..\.\..\.\..\)"; - ASSERT_EQ(TRUE, usvfsVirtualLinkFile(REAL_FILEW, outFile, 0)); - ASSERT_EQ(TRUE, usvfsVirtualLinkDirectoryStatic(REAL_DIRW, outDir, 0)); - - // both file and dir exist and have the correct type - ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outFile)); - ASSERT_NE(INVALID_FILE_ATTRIBUTES, usvfs::hook_GetFileAttributesW(outDir)); - ASSERT_EQ(0UL, usvfs::hook_GetFileAttributesW(outFile) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDir) & FILE_ATTRIBUTE_DIRECTORY); - ASSERT_NE(0UL, usvfs::hook_GetFileAttributesW(outDirCanonizeTest) & - FILE_ATTRIBUTE_DIRECTORY); -} - -int main(int argc, char** argv) -{ - using namespace test; - - auto dllPath = path_of_usvfs_lib(platform_dependant_executable("usvfs", "dll")); - ScopedLoadLibrary loadDll(dllPath.c_str()); - if (!loadDll) { - std::wcerr << L"failed to load usvfs dll: " << dllPath.c_str() << L", " - << GetLastError() << std::endl; - return 1; - } - - // note: this makes the logger available only to functions statically linked to the - // test binary, not those called in the dll - auto logger = spdlog::stdout_logger_mt("usvfs"); - logger->set_level(spdlog::level::warn); - testing::InitGoogleTest(&argc, argv); - int res = RUN_ALL_TESTS(); - - return res; -} diff --git a/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt b/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt deleted file mode 100644 index 88ff0c2..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) -find_package(Boost CONFIG REQUIRED COMPONENTS filesystem) - -# other executables -add_executable(usvfs_global_test - usvfs_global_test/usvfs_global_test.cpp -) -usvfs_set_test_properties(usvfs_global_test FOLDER usvfs_global_test) -target_link_libraries(usvfs_global_test PRIVATE gtest_utils GTest::gtest_main GTest::gmock Boost::filesystem) - -# actual test executable -add_executable(usvfs_global_test_runner - usvfs_global_test_fixture.cpp - usvfs_global_test_fixture.h - usvfs_global_test_runner.cpp -) -usvfs_set_test_properties(usvfs_global_test_runner FOLDER usvfs_global_test) -usvfs_target_link_usvfs(usvfs_global_test_runner) -target_link_libraries(usvfs_global_test_runner - PRIVATE test_utils gtest_utils GTest::gtest_main) -add_dependencies(usvfs_global_test_runner usvfs_global_test) diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp deleted file mode 100644 index 79c6c4c..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test/usvfs_global_test.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include <gtest/gtest.h> - -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> - -#include <filesystem> -#include <fstream> - -#include <boost/filesystem.hpp> - -#include <gmock/gmock-matchers.h> -#include <gtest_utils.h> - -// anonymous class that allow tests to access parameters (currently only where the -// virtualized data folder is) -// -class -{ - bool initialize(int argc, char* argv[]) - { - // extract the data path - if (argc != 2) { - std::cerr << "missing data path, aborting\n"; - return false; - } - m_data = std::filesystem::path{argv[1]}; - - if (!exists(m_data)) { - std::cerr << "data path '" << m_data.string() << "'does not exist\n"; - return false; - } - - return true; - } - - friend int main(int argc, char* argv[]); - std::filesystem::path m_data; - -public: - const auto& data() const { return m_data; } -} parameters; - -// simple guard for handle -class HandleGuard -{ - HANDLE m_handle = INVALID_HANDLE_VALUE; - -public: - HandleGuard() = default; - HandleGuard(HANDLE handle) : m_handle{handle} {} - - ~HandleGuard() { close(); } - - operator HANDLE() { return m_handle; } - - void close() - { - if (m_handle != INVALID_HANDLE_VALUE) { - ::CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; - } - } -}; - -// simple function to write content to a specified path -void write_content(const std::filesystem::path& path, const std::string_view content) -{ - std::ofstream ofs{path}; - ofs << content; -} - -TEST(BasicTest, SimpleTest) -{ - const auto data = parameters.data(); - - ASSERT_TRUE(exists(data)); - ASSERT_TRUE(exists(data / "docs")); - ASSERT_TRUE(exists(data / "empty")); - ASSERT_TRUE(exists(data / "docs" / "doc.txt")); - ASSERT_TRUE(exists(data / "readme.txt")); - - // should remove mods/mod1/info.txt - ASSERT_TRUE(exists(data / "info.txt")); - remove(data / "info.txt"); - ASSERT_FALSE(exists(data / "info.txt")); - - { - // retrieve the path of data using GetFinalPathNameByHandleW() to - // compare later on - std::filesystem::path dataPathFromHandle; - { - HandleGuard hdl = CreateFileW(data.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - WCHAR filepath[1024]; - const auto length = GetFinalPathNameByHandleW( - hdl, filepath, sizeof(filepath) / sizeof(WCHAR), FILE_NAME_NORMALIZED); - ASSERT_NE(0, length); - - dataPathFromHandle = std::filesystem::path(std::wstring(filepath, length)); - } - - const auto doc_txt = data / "docs" / "doc.txt"; - HandleGuard hdl = CreateFileW(doc_txt.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - WCHAR filepath[1024]; - const auto length = GetFinalPathNameByHandleW( - hdl, filepath, sizeof(filepath) / sizeof(WCHAR), FILE_NAME_NORMALIZED); - ASSERT_NE(0, length); - - ASSERT_EQ(dataPathFromHandle / "docs" / "doc.txt", - std::filesystem::path(std::wstring(filepath, length))); - } - - { - // retrieve the path of data using GetFileInformationByHandleEx() to - // compare later on - std::filesystem::path dataPathFromHandle; - { - HandleGuard hdl = CreateFileW(data.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - char buffer[4096]; - ASSERT_TRUE( - GetFileInformationByHandleEx(hdl, FileNameInfo, buffer, sizeof(buffer))); - auto* info = reinterpret_cast<FILE_NAME_INFO*>(buffer); - - dataPathFromHandle = std::filesystem::path( - std::wstring_view(info->FileName, info->FileNameLength / 2)); - } - - const auto info_txt = data / "readme.txt"; - HandleGuard hdl = CreateFileW(info_txt.c_str(), GENERIC_READ, 0, nullptr, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - ASSERT_NE(INVALID_HANDLE_VALUE, (HANDLE)hdl); - - char buffer[4096]; - ASSERT_TRUE( - GetFileInformationByHandleEx(hdl, FileNameInfo, buffer, sizeof(buffer))); - - auto* info = reinterpret_cast<FILE_NAME_INFO*>(buffer); - - ASSERT_EQ(dataPathFromHandle / "readme.txt", - std::filesystem::path( - std::wstring_view(info->FileName, info->FileNameLength / 2))); - } -} - -TEST(BoostFilesystemTest, BoostFilesystemTest) -{ - using namespace boost::filesystem; - - const path data{parameters.data().native()}; - - std::vector<std::string> contents; - for (recursive_directory_iterator it{data}; it != recursive_directory_iterator{}; - ++it) { - contents.push_back(relative(it->path(), data).string()); - } - ASSERT_THAT(contents, ::testing::UnorderedElementsAre( - ".gitkeep", "docs", "docs\\doc.txt", "docs\\subdocs", - "docs\\subdocs\\.gitkeep")); -} - -// see https://github.com/ModOrganizer2/modorganizer/issues/2039 for context -// -TEST(RedFileSystemTest, RedFileSystemTest) -{ - const auto data = parameters.data(); - - const auto storages_path = data / "r6" / "storages"; - const auto hudpainter_path = storages_path / "HUDPainter"; - - ASSERT_TRUE(exists(hudpainter_path / "DEFAULT.json")); - - // TEST.json does not exist, so will be created in overwrite - this mainly check that - // weakly_canonical returns the path under data/ and not the actual path under - // overwrite/ - // - // this relies on the hook for NtQueryInformationFile - // - ASSERT_FALSE(exists(hudpainter_path / "TEST.json")); - ASSERT_EQ(hudpainter_path / "TEST.json", - weakly_canonical(hudpainter_path / "TEST.json")); - write_content(hudpainter_path / "TEST.json", "{}"); -} - -TEST(SkipFilesTest, SkipFilesTest) -{ - const auto data = parameters.data(); - - // file in mod1 should have been skipped - ASSERT_FALSE(exists(data / "readme.skip")); - - // docs/doc.skip should come from data, not mods1/docs/doc.skip - ASSERT_CONTENT_EQ("doc.skip in data/docs", data / "docs" / "doc.skip"); - - // write to readme.skip should create a file in overwrite - write_content(data / "readme.skip", "readme.skip in overwrite"); -} - -int main(int argc, char* argv[]) -{ - testing::InitGoogleTest(&argc, argv); - - // initialize parameters from remaining arguments - if (!parameters.initialize(argc, argv)) { - return -1; - } - - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp deleted file mode 100644 index 4f6e672..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include "usvfs_global_test_fixture.h" - -#include <filesystem> -#include <format> -#include <fstream> - -#include <gtest/gtest.h> - -#include <stringcast.h> -#include <test_helpers.h> -#include <usvfs.h> -#include <windows_sane.h> - -// find the path to the executable that contains gtest entries -// -std::filesystem::path path_to_usvfs_global_test() -{ - return test::path_of_test_bin( - test::platform_dependant_executable("usvfs_global_test", "exe")); -} - -// path to the fixture for the given test group -// -std::filesystem::path path_to_usvfs_global_test_figures(std::wstring_view group) -{ - return test::path_of_test_fixtures() / "usvfs_global_test" / group; -} - -// spawn the an hook version of the given -// -DWORD spawn_usvfs_hooked_process( - const std::filesystem::path& app, const std::vector<std::wstring>& arguments = {}, - const std::optional<std::filesystem::path>& working_directory = {}) -{ - using namespace usvfs::shared; - - std::wstring command = app; - std::filesystem::path cwd = working_directory.value_or(app.parent_path()); - std::vector<wchar_t> env; - - if (!arguments.empty()) { - command += L" " + boost::algorithm::join(arguments, L" "); - } - - STARTUPINFO si{0}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{0}; - -#pragma warning(push) -#pragma warning(disable : 6387) - - if (!usvfsCreateProcessHooked(nullptr, command.data(), nullptr, nullptr, FALSE, 0, - nullptr, cwd.c_str(), &si, &pi)) { - test::throw_testWinFuncFailed( - "CreateProcessHooked", - string_cast<std::string>(command, CodePage::UTF8).c_str()); - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exit = 99; - if (!GetExitCodeProcess(pi.hProcess, &exit)) { - test::WinFuncFailedGenerator failed; - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - throw failed("GetExitCodeProcess"); - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - -#pragma warning(pop) - - return exit; -} - -bool UsvfsGlobalTest::m_force_usvfs_logs = false; - -void UsvfsGlobalTest::ForceUsvfsLogs() -{ - m_force_usvfs_logs = true; -} - -class UsvfsGlobalTest::UsvfsGuard -{ -public: - UsvfsGuard(std::string_view instance_name = "usvfs_test", bool logging = false) - : m_parameters(usvfsCreateParameters(), &usvfsFreeParameters) - { - usvfsSetInstanceName(m_parameters.get(), instance_name.data()); - usvfsSetDebugMode(m_parameters.get(), false); - usvfsSetLogLevel(m_parameters.get(), LogLevel::Debug); - usvfsSetCrashDumpType(m_parameters.get(), CrashDumpsType::None); - usvfsSetCrashDumpPath(m_parameters.get(), ""); - - usvfsInitLogging(logging); - usvfsCreateVFS(m_parameters.get()); - } - - ~UsvfsGuard() { usvfsDisconnectVFS(); } - -private: - std::unique_ptr<usvfsParameters, decltype(&usvfsFreeParameters)> m_parameters; -}; - -UsvfsGlobalTest::UsvfsGlobalTest() - : m_usvfs{std::make_unique<UsvfsGuard>()}, - m_temporary_folder{test::path_of_test_temp()} -{ - std::string name{testing::UnitTest::GetInstance()->current_test_info()->name()}; - m_group = {name.begin(), name.end()}; -} - -UsvfsGlobalTest::~UsvfsGlobalTest() -{ - CleanUp(); -} - -std::filesystem::path UsvfsGlobalTest::ActualFolder() const -{ - return m_temporary_folder; -} - -std::filesystem::path UsvfsGlobalTest::ExpectedFolder() const -{ - return path_to_usvfs_global_test_figures(m_group) / "expected"; -} - -void UsvfsGlobalTest::CleanUp() const -{ - if (exists(m_temporary_folder)) { - remove_all(m_temporary_folder); - } -} - -void UsvfsGlobalTest::PrepareFileSystem() const -{ - // cleanup in case a previous tests failed to delete its stuff - CleanUp(); - - // copy fixtures - const auto fixtures = path_to_usvfs_global_test_figures(m_group) / "source"; - if (exists(fixtures)) { - copy(fixtures, m_temporary_folder, std::filesystem::copy_options::recursive); - } -} - -void UsvfsGlobalTest::SetUpOverwrite(bool force) const -{ - if (force && !exists(m_overwrite_folder)) { - create_directory(m_overwrite_folder); - } - - if (exists(m_overwrite_folder)) { - usvfsVirtualLinkDirectoryStatic(m_overwrite_folder.c_str(), m_data_folder.c_str(), - LINKFLAG_CREATETARGET | LINKFLAG_RECURSIVE); - } -} - -void UsvfsGlobalTest::PrepareMapping() const -{ - // should not be needed, but just to be safe - usvfsClearVirtualMappings(); - - if (!exists(m_data_folder)) { - throw std::runtime_error{ - std::format("data path missing at {}", m_data_folder.string())}; - } - - if (exists(m_mods_folder)) { - for (const auto& mod : std::filesystem::directory_iterator(m_mods_folder)) { - if (!is_directory(mod)) { - continue; - } - usvfsVirtualLinkDirectoryStatic(mod.path().c_str(), m_data_folder.c_str(), - LINKFLAG_RECURSIVE); - } - } - - // by default, only create overwrite if present - SetUpOverwrite(false); -} - -int UsvfsGlobalTest::Run() const -{ - PrepareMapping(); - - const auto res = spawn_usvfs_hooked_process( - path_to_usvfs_global_test(), {std::format(L"--gtest_filter={}.*", m_group), - L"--gtest_brief=1", m_data_folder.native()}); - - // TODO: try to do this with gtest itself? - if (m_force_usvfs_logs || res != 0) { - const auto log_path = test::path_of_test_bin(m_group + L".log"); - std::ofstream os{log_path}; - std::string buffer(1024, '\0'); - std::cout << "process returned " << std::hex << res << ", usvfs logs dumped to " - << log_path.string() << '\n'; - while (usvfsGetLogMessages(buffer.data(), buffer.size(), false)) { - os << " " << buffer.c_str() << "\n"; - } - } - - return res; -} diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h deleted file mode 100644 index aeafc25..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_fixture.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include <filesystem> -#include <memory> - -#include <gtest/gtest.h> - -class UsvfsGlobalTest : public testing::Test -{ -public: - // enable log mode - this will generate USVFS log file for all tests regardless of - // success or failure (default is to only generate for failure) - // - static void ForceUsvfsLogs(); - -public: - UsvfsGlobalTest(); - - void SetUp() override { PrepareFileSystem(); } - - void TearDown() override { CleanUp(); } - - ~UsvfsGlobalTest(); - - // setup the overwrite folder - // - // if force is true, force creation of the overwrite folder even if not present, this - // is useful when the overwrite is initially empty and thus cannot be committed to git - // but need to contain files after the run - // - void SetUpOverwrite(bool force = true) const; - - // run the test, return the exit code of the google test process - // - int Run() const; - - // return the path to the folder containing the expected results - // - std::filesystem::path ActualFolder() const; - - // return the path to the folder containing the expected results - // - std::filesystem::path ExpectedFolder() const; - -private: - class UsvfsGuard; - - // always generate usvfs logs - static bool m_force_usvfs_logs; - - // prepare the filesystem by copying files and folders from the relevant fixtures - // folder to the temporary folder - // - // after this operations, the temporary folder will contain - // - a data folder - // - [optional] a mods folder containing a set of folders that should be mounted - // - [optional] an overwrite folder that should be mounted as overwrite - // - void PrepareFileSystem() const; - - // prepare mapping using the given set of paths - // - void PrepareMapping() const; - - // cleanup the temporary path - // - void CleanUp() const; - - // usvfs_guard - std::unique_ptr<UsvfsGuard> m_usvfs; - - // name of GTest group (first argument of the TEST macro) to run - std::wstring m_group; - - // path to the folder containing temporary files - std::filesystem::path m_temporary_folder; - - // path to the subfolder inside the temporary folder - std::filesystem::path m_data_folder = m_temporary_folder / "data"; - std::filesystem::path m_mods_folder = m_temporary_folder / "mods"; - std::filesystem::path m_overwrite_folder = m_temporary_folder / "overwrite"; -}; diff --git a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp b/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp deleted file mode 100644 index 2fa6cc8..0000000 --- a/libs/usvfs/test/usvfs_global_test_runner/usvfs_global_test_runner.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include <gtest/gtest.h> - -#include <gtest_utils.h> -#include <test_helpers.h> -#include <usvfs.h> - -#include "usvfs_global_test_fixture.h" - -TEST_F(UsvfsGlobalTest, BasicTest) -{ - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, BoostFilesystemTest) -{ - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, RedFileSystemTest) -{ - SetUpOverwrite(true); - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -TEST_F(UsvfsGlobalTest, SkipFilesTest) -{ - SetUpOverwrite(true); - - usvfsAddSkipFileSuffix(L".skip"); - - ASSERT_EQ(0, Run()); - ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); -} - -int main(int argc, char* argv[]) -{ - // load the USVFS DLL - // - const auto usvfs_dll = - test::path_of_usvfs_lib(test::platform_dependant_executable("usvfs", "dll")); - test::ScopedLoadLibrary loadDll(usvfs_dll.c_str()); - if (!loadDll) { - std::wcerr << L"ERROR: failed to load usvfs dll: " << usvfs_dll.c_str() << L", " - << GetLastError() << L"\n"; - return 3; - } - - testing::InitGoogleTest(&argc, argv); - - UsvfsGlobalTest::ForceUsvfsLogs(); - - usvfsInitLogging(false); - - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/test/usvfs_test_runner/CMakeLists.txt b/libs/usvfs/test/usvfs_test_runner/CMakeLists.txt deleted file mode 100644 index a0a7ca9..0000000 --- a/libs/usvfs/test/usvfs_test_runner/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -find_package(GTest CONFIG REQUIRED) - -# other executables -add_executable(test_file_operations - test_file_operations/test_file_operations.cpp - test_file_operations/test_filesystem.cpp - test_file_operations/test_filesystem.h - test_file_operations/test_ntapi.cpp - test_file_operations/test_ntapi.h - test_file_operations/test_ntdll_declarations.h - test_file_operations/test_w32api.cpp - test_file_operations/test_w32api.h -) -usvfs_set_test_properties(test_file_operations FOLDER usvfs_test_runner) -target_link_libraries(test_file_operations PRIVATE test_utils ntdll) - -add_executable(usvfs_test - usvfs_test/usvfs_basic_test.cpp - usvfs_test/usvfs_basic_test.h - usvfs_test/usvfs_test_base.cpp - usvfs_test/usvfs_test_base.h - usvfs_test/usvfs_test.cpp -) -usvfs_set_test_properties(usvfs_test FOLDER usvfs_test_runner) -usvfs_target_link_usvfs(usvfs_test) -target_link_libraries(usvfs_test PRIVATE test_utils) - -# actual test executable -add_executable(usvfs_test_runner usvfs_test_runner.cpp) -usvfs_set_test_properties(usvfs_test_runner FOLDER usvfs_test_runner) -target_link_libraries(usvfs_test_runner - PRIVATE test_utils GTest::gtest GTest::gtest_main) -add_dependencies(usvfs_test_runner usvfs_test test_file_operations) diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_file_operations.cpp b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_file_operations.cpp deleted file mode 100644 index 2135df3..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_file_operations.cpp +++ /dev/null @@ -1,496 +0,0 @@ -#include <cstdio> -#include <stdexcept> - -#include <boost/filesystem.hpp> -#include <boost/type_traits.hpp> -#include <winapi.h> - -#include "test_ntapi.h" -#include "test_w32api.h" -#include <test_helpers.h> - -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> - -using usvfs::shared::CodePage; -using usvfs::shared::string_cast; - -void print_usage(const char* myname) -{ - using namespace std; - fprintf(stderr, "usage: %s [<options>] <command> [<command params>] ...\n", myname); - fprintf(stderr, - "options and commands are parsed and executed in the order they appear.\n"); - fprintf(stderr, "\nsupported commands:\n"); - fprintf( - stderr, - " -list <dir> : lists the given directory and outputs the results.\n"); - fprintf(stderr, " -listcontents <dir> : lists the given directory, reading all files " - "and outputs the results.\n"); - fprintf(stderr, - " -read <file> : reads the given file and outputs the results.\n"); - fprintf(stderr, " -overwrite <file> <string> : overwrites the file at the given path " - "with the given line (creating directories if in recursive mode).\n"); - fprintf(stderr, " -deleteoverwrite <file> <string> : shorthand for -delete <file> " - "-overwrite <file> <string>\n"); - fprintf(stderr, - " -rewrite <file> <string> : rewrites the file at the given path with the " - "given line (fails if file doesn't exist; uses read/write access).\n"); - fprintf(stderr, - " -touch <file> : updates last write timestamp of specified file.\n"); - fprintf(stderr, " -touchw <file> : updates last write timestamp using full " - "write permissions.\n"); - fprintf(stderr, " -delete <file> : deletes the given file.\n"); - fprintf(stderr, " -copy <src> <dst> : copies the given file.\n"); - fprintf(stderr, " -copyover <src> <dst> : copies the given file (replacing existing " - "destination).\n"); - fprintf(stderr, " -rename <src> <dst> : renames the given file.\n"); - fprintf(stderr, " -renameover <src> <dst> : renames the given file (replacing " - "existing destination).\n"); - fprintf(stderr, " -deleterename <src> <dst> : shorthand for -delete <dst> -rename " - "<src> <dst>.\n"); - fprintf(stderr, - " -move <src> <dst> : moves the given file (not supported by ntapi).\n"); - fprintf(stderr, " -moveover <src> <dst> : moves the given file (replacing existing " - "destination; not supported by ntapi).\n"); - fprintf( - stderr, - " -deletemove <src> <dst> : shorthand for -delete <dst> -move <src> <dst>.\n"); - fprintf(stderr, " -debug : shows a message box and wait for a debugger " - "to connect.\n"); - fprintf(stderr, "\nsupported options:\n"); - fprintf(stderr, " -out <file> : file to log output to (use \"-\" for the " - "stdout; otherwise path to output should exist).\n"); - fprintf(stderr, " -out+ <file> : similar to -out but appends the file instead " - "of overwriting it.\n"); - fprintf(stderr, " -cout <file> : similar to -out but does not log PID and " - "other info which may change between runs.\n"); - fprintf(stderr, " -cout+ <file> : similar to -cout but appends the file " - "instead of overwriting it.\n"); - fprintf(stderr, " -r : recursively list/create directories.\n"); - fprintf(stderr, - " -r- : don't recursively list/create directories.\n"); - fprintf(stderr, " -basedir <dir> : any paths under the basedir will outputed in " - "a relative manner (default is current directory).\n"); - fprintf(stderr, - " -w32api : use regular Win32 API for file access (default).\n"); - fprintf(stderr, - " -ntapi : use lower level ntdll functions for file access.\n"); -} - -class CommandExecuter -{ -public: - CommandExecuter() : m_output(stdout), m_api(&w32api) - { - set_basedir(TestFileSystem::current_directory().string().c_str()); - } - - ~CommandExecuter() - { - if (m_output && m_output != stdout) - fclose(m_output); - } - - FILE* output() const { return m_output; } - - bool file_output() const { return m_output != stdout; } - - // Options: - - void set_output(const char* output_file, bool clean, bool append, const char* cmdline) - { - if (m_output && m_output != stdout) { - fprintf(m_output, "#< Output log closed.\n", output_file); - fclose(m_output); - } - - m_cleanoutput = clean; - m_output = nullptr; - errno_t err = fopen_s(&m_output, output_file, append ? "at" : "wt"); - if (err || !m_output) - test::throw_testWinFuncFailed("fopen_s", output_file, err); - else { - if (m_cleanoutput) - fprintf(m_output, "#> Output log openned for: %s\n", - clean_cmdline_heuristic(cmdline).c_str()); - else - fprintf(m_output, "#> Output log openned for (pid %d): %s\n", - GetCurrentProcessId(), cmdline); - w32api.set_output(m_output, m_cleanoutput); - ntapi.set_output(m_output, m_cleanoutput); - } - } - - bool cleanoutput() const { return m_cleanoutput; } - - void set_recursive(bool recursive) { m_recursive = recursive; } - - void set_basedir(const char* basedir) - { - w32api.set_basepath(basedir); - ntapi.set_basepath(basedir); - } - - void set_ntapi(bool enable) - { - if (enable) - m_api = &ntapi; - else - m_api = &w32api; - } - - // Commands: - - void list(const char* dir, bool read_files) - { - if (debug_pending()) - __debugbreak(); - - list_impl(m_api->real_path(dir), read_files); - } - - void read(const char* path) - { - if (debug_pending()) - __debugbreak(); - - m_api->read_file(m_api->real_path(path)); - } - - void overwrite(const char* path, const char* value) - { - if (debug_pending()) - __debugbreak(); - - const auto& real = real_path_create_if_necessary(path); - - m_api->write_file(real, value, strlen(value), true, - TestFileSystem::write_mode::overwrite); - } - - void rewrite(const char* path, const char* value) - { - if (debug_pending()) - __debugbreak(); - - const auto& real = real_path_create_if_necessary(path); - - // Use read/write access when rewriting to "simulate" the harder case where it is - // not known if the file is going to actually be changed - m_api->write_file(real, value, strlen(value), false, - TestFileSystem::write_mode::manual_truncate, true); - m_api->write_file(real, "\r\n", 2, false, TestFileSystem::write_mode::append); - } - - void touch(const char* path, bool full_write_access) - { - if (debug_pending()) - __debugbreak(); - - const auto& real = real_path_create_if_necessary(path); - - m_api->touch_file(real, full_write_access); - } - - void deletef(const char* path) - { - if (debug_pending()) - __debugbreak(); - - m_api->delete_file(m_api->real_path(path)); - } - - void rename(const char* source, const char* destination, bool replace_existing) - { - if (debug_pending()) - __debugbreak(); - - m_api->copy_file(m_api->real_path(source), m_api->real_path(destination), - replace_existing); - } - - void rename(const char* source, const char* destination, bool replace_existing, - bool allow_copy) - { - if (debug_pending()) - __debugbreak(); - - m_api->rename_file(m_api->real_path(source), m_api->real_path(destination), - replace_existing, allow_copy); - } - - void debug() { m_debug_pending = true; } - - bool debug_pending() - { - if (!m_debug_pending) - return false; - m_debug_pending = false; - if (!IsDebuggerPresent()) - MessageBoxA(NULL, "Connect a debugger and press OK to trigger a breakpoint", - "DEBUG", 0); - return IsDebuggerPresent(); - } - - // Traversal: - - void list_impl(TestFileSystem::path real, bool read_files) - { - std::vector<TestFileSystem::path> recurse; - { - auto files = m_api->list_directory(real); - fprintf(m_output, ">> Listing directory {%s}:\n", - m_api->relative_path(real).string().c_str()); - for (auto f : files) { - if (f.is_dir()) { - fprintf(m_output, "[%s] DIR (attributes 0x%x)\n", - string_cast<std::string>(f.name, CodePage::UTF8).c_str(), - f.attributes); - if (m_recursive && f.name != L"." && f.name != L"..") - recurse.push_back(real / f.name); - } else { - fprintf(m_output, "[%s] FILE (attributes 0x%x, %lld bytes)\n", - string_cast<std::string>(f.name, CodePage::UTF8).c_str(), - f.attributes, f.size); - if (read_files) - m_api->read_file(real / f.name); - } - } - } - for (auto r : recurse) - list_impl(r, read_files); - } - -private: - TestFileSystem::path real_path_create_if_necessary(const char* path) - { - auto real = m_api->real_path(path); - if (m_recursive) - try { - m_api->create_path(real.parent_path()); - } catch (const std::exception& e) { - throw std::runtime_error( - std::format("Failed to create_path [{}] : {}", - m_api->relative_path(real.parent_path()).string(), e.what())); - } - return std::move(real); - } - - std::string clean_cmdline_arg(const char* arg_start, const char* arg_end) - { - if (arg_start == arg_end) - return std::string(); - bool quoted = *arg_start == '\"' && *(arg_end - 1) == '\"'; - const char* last_sep = arg_end; - while (last_sep != arg_start && *last_sep != '\\') - --last_sep; - if (arg_end - arg_start < (quoted ? 5 : 3) || arg_start[0] == '-' || - arg_start[quoted ? 2 : 1] != ':' || last_sep == arg_start) - return std::string(arg_start, arg_end); - std::string res = quoted ? "\"" : ""; - res.append(last_sep + 1, arg_end); - return res; - } - - std::string clean_cmdline_heuristic(const char* cmdline) - { - std::string res; - bool first = true; - while (*cmdline) { - const char* end = strchr(cmdline, ' '); - if (!end) - end = cmdline + strlen(cmdline); - if (first) - first = false; - else - res.push_back(' '); - res += clean_cmdline_arg(cmdline, end); - cmdline = end; - while (*cmdline == ' ') - ++cmdline; - } - return res; - } - - FILE* m_output; - bool m_cleanoutput = false; - bool m_recursive = false; - bool m_debug_pending = false; - - TestFileSystem* m_api; - static TestW32Api w32api; - static TestNtApi ntapi; -}; - -// static -TestW32Api CommandExecuter::w32api(stdout); -TestNtApi CommandExecuter::ntapi(stdout); - -class abort_invalid_argument : std::exception -{}; - -bool verify_args_exist(const char* flag, int params, int index, int count) -{ - if (index + params >= count) { - fprintf(stderr, "ERROR: %s requires %d arguments\n", flag, params); - throw abort_invalid_argument(); - } - return true; -} - -const char* UntouchedCommandLineArguments() -{ - const char* cmd = GetCommandLineA(); - for (; *cmd && *cmd != ' '; ++cmd) { - if (*cmd == '"') { - int escaped = 0; - for (++cmd; *cmd && (*cmd != '"' || escaped % 2 != 0); ++cmd) - escaped = *cmd == '\\' ? escaped + 1 : 0; - } - } - while (*cmd == ' ') - ++cmd; - return cmd; -} - -int main(int argc, char* argv[]) -{ - bool found_commands = false; - CommandExecuter executer; - - TestFileSystem::path exe_path = argv[0]; - std::string exename = exe_path.filename().string(); - std::string cmdline = exename + " " + UntouchedCommandLineArguments(); - fprintf(stdout, "#> process %d started with commandline: %s\n", GetCurrentProcessId(), - cmdline.c_str()); - - for (int ai = 1; ai < argc; ++ai) { - try { - SetLastError(0); - - // options: - if (strcmp(argv[ai], "-out") == 0 && verify_args_exist("-out", 1, ai, argc) || - strcmp(argv[ai], "-out+") == 0 && verify_args_exist("-out+", 1, ai, argc) || - strcmp(argv[ai], "-cout") == 0 && verify_args_exist("-cout", 1, ai, argc) || - strcmp(argv[ai], "-cout+") == 0 && verify_args_exist("-cout+", 1, ai, argc)) { - bool clean = argv[ai][1] == 'c'; - bool append = argv[ai][clean ? 5 : 4] == '+'; - executer.set_output(argv[++ai], clean, append, cmdline.c_str()); - } else if (strcmp(argv[ai], "-r") == 0) - executer.set_recursive(true); - else if (strcmp(argv[ai], "-r-") == 0) - executer.set_recursive(false); - else if (strcmp(argv[ai], "-basedir") == 0 && - verify_args_exist("-basedir", 1, ai, argc)) { - executer.set_basedir(argv[++ai]); - } else if (strcmp(argv[ai], "-w32api") == 0) - executer.set_ntapi(false); - else if (strcmp(argv[ai], "-ntapi") == 0) - executer.set_ntapi(true); - // commands: - else if ((strcmp(argv[ai], "-list") == 0 || - strcmp(argv[ai], "-listcontents") == 0) && - verify_args_exist("-list", 1, ai, argc)) { - bool contents = strcmp(argv[ai], "-listcontents") == 0; - executer.list(argv[++ai], contents); - found_commands = true; - } else if (strcmp(argv[ai], "-read") == 0 && - verify_args_exist("-read", 1, ai, argc)) { - executer.read(argv[++ai]); - found_commands = true; - } else if (strcmp(argv[ai], "-overwrite") == 0 && - verify_args_exist("-overwrite", 2, ai, argc) || - strcmp(argv[ai], "-deleteoverwrite") == 0 && - verify_args_exist("-deleteoverwrite", 2, ai, argc)) { - if (argv[ai][1] == 'd') - executer.deletef(argv[ai + 1]); - executer.overwrite(argv[ai + 1], argv[ai + 2]); - ++ ++ai; - found_commands = true; - } else if (strcmp(argv[ai], "-rewrite") == 0 && - verify_args_exist("-rewrite", 2, ai, argc)) { - executer.rewrite(argv[ai + 1], argv[ai + 2]); - ++ ++ai; - found_commands = true; - } else if (strcmp(argv[ai], "-touch") == 0 && - verify_args_exist("-touch", 1, ai, argc) || - strcmp(argv[ai], "-touchw") == 0 && - verify_args_exist("-touchw", 1, ai, argc)) { - bool write_access = argv[ai][6] == 'w'; - executer.touch(argv[++ai], write_access); - found_commands = true; - } else if (strcmp(argv[ai], "-delete") == 0 && - verify_args_exist("-delete", 1, ai, argc)) { - executer.deletef(argv[++ai]); - found_commands = true; - } else if (strcmp(argv[ai], "-copy") == 0 && - verify_args_exist("-copy", 2, ai, argc) || - strcmp(argv[ai], "-copyover") == 0 && - verify_args_exist("-copyover", 2, ai, argc)) { - bool over = argv[ai][5] == 'o'; - executer.rename(argv[ai + 1], argv[ai + 2], over); - ++ ++ai; - found_commands = true; - } else if (strcmp(argv[ai], "-rename") == 0 && - verify_args_exist("-rename", 2, ai, argc) || - strcmp(argv[ai], "-renameover") == 0 && - verify_args_exist("-renameover", 2, ai, argc) || - strcmp(argv[ai], "-deleterename") == 0 && - verify_args_exist("-deleterename", 2, ai, argc) || - strcmp(argv[ai], "-move") == 0 && - verify_args_exist("-move", 2, ai, argc) || - strcmp(argv[ai], "-moveover") == 0 && - verify_args_exist("-moveover", 2, ai, argc) || - strcmp(argv[ai], "-deletemove") == 0 && - verify_args_exist("-deletemove", 2, ai, argc)) { - bool del = argv[ai][1] == 'd'; - bool move = argv[ai][del ? 7 : 1] == 'm'; - bool over = !del && argv[ai][move ? 5 : 7] == 'o'; - if (del) - executer.deletef(argv[ai + 2]); - executer.rename(argv[ai + 1], argv[ai + 2], over, move); - ++ ++ai; - found_commands = true; - } else if (strcmp(argv[ai], "-debug") == 0) { - executer.debug(); - } else { - if (executer.file_output()) - fprintf(executer.output(), "ERROR: invalid argument {%s}\n", argv[ai]); - fprintf(stderr, "ERROR: invalid argument {%s}\n", argv[ai]); - return 1; - } - } catch (const abort_invalid_argument&) { - return 1; - } -#if 1 // just a convient way to not catch exception when debugging - catch (const std::exception& e) { - if (executer.file_output()) - fprintf(executer.output(), "ERROR: %hs\n", e.what()); - fprintf(stderr, "ERROR: %hs\n", e.what()); - return 1; - } catch (...) { - if (executer.file_output()) - fprintf(executer.output(), "ERROR: unknown exception"); - fprintf(stderr, "ERROR: unknown exception\n"); - return 1; - } -#endif - } - - if (!found_commands) { - print_usage(exename.c_str()); - return 2; - } - - if (executer.file_output()) - if (executer.cleanoutput()) - fprintf(executer.output(), "#< %s ended properly.\n", exename.c_str()); - else - fprintf(executer.output(), "#< %s ended properly in process %d.\n", - exename.c_str(), GetCurrentProcessId()); - fprintf(stdout, "#< %s ended properly in process %d.\n", exename.c_str(), - GetCurrentProcessId()); - - return 0; -} diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.cpp b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.cpp deleted file mode 100644 index dcfd5a1..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.cpp +++ /dev/null @@ -1,159 +0,0 @@ - -#include "test_filesystem.h" -#include <test_helpers.h> -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> - -using test::throw_testWinFuncFailed; - -bool TestFileSystem::FileInformation::is_dir() const -{ - return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -bool TestFileSystem::FileInformation::is_file() const -{ - return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0; -} - -TestFileSystem::TestFileSystem(FILE* output) : m_output(output) {} - -TestFileSystem::path TestFileSystem::current_directory() -{ - DWORD res = GetCurrentDirectoryW(0, NULL); - if (!res) - throw_testWinFuncFailed("GetCurrentDirectory", res); - std::wstring buf(res + 1, '\0'); - res = GetCurrentDirectoryW(buf.length(), &buf[0]); - if (!res || res >= buf.length()) - throw_testWinFuncFailed("GetCurrentDirectory", res); - buf.resize(res); - return buf; -} - -TestFileSystem::path TestFileSystem::relative_path(path full_path) -{ - return test::path_as_relative(m_basepath, full_path); -} - -// static -const char* TestFileSystem::write_operation_name(write_mode mode) -{ - switch (mode) { - case write_mode::manual_truncate: - return "Writing file (by open & truncate)"; - case write_mode::truncate: - return "Truncating file"; - case write_mode::create: - return "Creating file"; - case write_mode::overwrite: - return "Overwriting file"; - case write_mode::opencreate: - return "Opening/Creating file"; - case write_mode::append: - return "Appending file"; - } - return "Unknown write operation?!"; -} - -// static -const char* TestFileSystem::rename_operation_name(bool replace_existing, - bool allow_copy) -{ - if (allow_copy) - return replace_existing ? "Moving file over" : "Moving file"; - else - return replace_existing ? "Renaming file over" : "Renaming file"; -} - -void TestFileSystem::print_operation(const char* operation, const path& target) -{ - if (m_output) - fprintf(m_output, "# (%s) %s {%s}\n", id(), operation, - relative_path(target).string().c_str()); -} - -void TestFileSystem::print_operation(const char* operation, const path& source, - const path& target) -{ - if (m_output) - fprintf(m_output, "# (%s) %s {%s} {%s}\n", id(), operation, - relative_path(source).string().c_str(), - relative_path(target).string().c_str()); -} - -static inline void print_op_with_result(FILE* output, const char* prefix, - const char* operation, const uint32_t* result, - DWORD* last_error, const char* opt_arg) -{ - if (output) { - fprintf(output, "%s%s", prefix, operation); - if (opt_arg) - fprintf(output, " %s", opt_arg); - if (result) - fprintf(output, " returned %u (0x%x)", *result, *result); - if (last_error) - fprintf(output, " last error %u (0x%x)", *last_error, *last_error); - fprintf(output, "\n"); - } -} - -void TestFileSystem::print_result(const char* operation, uint32_t result, - bool with_last_error, const char* opt_arg, - bool hide_result) -{ - if (m_output) { - DWORD last_error = GetLastError(); - std::string prefix = "# ("; - prefix += id(); - prefix += ") "; - print_op_with_result(m_output, prefix.c_str(), operation, - hide_result ? nullptr : &result, - with_last_error ? &last_error : nullptr, opt_arg); - SetLastError(last_error); - } -} - -void TestFileSystem::print_error(const char* operation, uint32_t result, - bool with_last_error, const char* opt_arg) -{ - DWORD last_error = with_last_error ? GetLastError() : 0; - print_op_with_result(stderr, "ERROR: ", operation, &result, - with_last_error ? &last_error : nullptr, opt_arg); - if (m_output && m_output != stdout) - print_op_with_result(m_output, "ERROR: ", operation, &result, - with_last_error ? &last_error : nullptr, opt_arg); -} - -void TestFileSystem::print_write_success(const void* data, std::size_t size, - std::size_t written) -{ - if (m_output) { - fprintf(m_output, "# Successfully written %u bytes ", - static_cast<unsigned>(written)); - // heuristics to print nicer one liners: - if (size == 1 && reinterpret_cast<const char*>(data)[0] == '\n' || - size == 2 && reinterpret_cast<const char*>(data)[0] == '\r' && - reinterpret_cast<const char*>(data)[1] == '\n') - fprintf(m_output, "<newline>"); - else { - fprintf(m_output, "{"); - if (size && reinterpret_cast<const char*>(data)[size - 1] == '\n') - --size; - if (data) - fwrite(data, 1, size, m_output); - else if (size) - fwrite("NULL?!", 1, 6, m_output); - fprintf(m_output, "}"); - } - fprintf(output(), "\n"); - } -} - -uint32_t TestFileSystem::clean_attributes(uint32_t attr) -{ - if (m_cleanoutput) - return attr & ~(FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); - else - return attr; -} diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.h b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.h deleted file mode 100644 index 3a5f53b..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_filesystem.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include <cstdio> -#include <filesystem> -#include <string> -#include <vector> - -class TestFileSystem -{ -public: - static constexpr auto FILE_CONTENTS_PRINT_PREFIX = "== "; - - typedef std::filesystem::path path; - typedef std::FILE FILE; - - static path current_directory(); - - TestFileSystem(FILE* output); - - void set_output(FILE* output, bool clean) - { - m_output = output; - m_cleanoutput = clean; - } - - // base path used to trim outputs (which is important so we can compare tests ran at - // different base paths) - void set_basepath(const char* path) { m_basepath = real_path(path); } - - // returns the path relative to the base path - path relative_path(path full_path); - - virtual const char* id() = 0; - - virtual path real_path(const char* abs_or_rel_path) = 0; - - struct FileInformation - { - std::wstring name; - uint32_t attributes; - uint64_t size; - - FileInformation(const std::wstring& iname, uint32_t iattributes, uint64_t isize) - : name(iname), attributes(iattributes), size(isize) - {} - - bool is_dir() const; - bool is_file() const; - }; - typedef std::vector<FileInformation> FileInfoList; - - virtual FileInfoList list_directory(const path& directory_path) = 0; - - virtual void create_path(const path& directory_path) = 0; - - virtual void read_file(const path& file_path) = 0; - - enum class write_mode - { - manual_truncate, - truncate, - create, - overwrite, - opencreate, - append - }; - virtual void write_file(const path& file_path, const void* data, std::size_t size, - bool add_new_line, write_mode mode, - bool rw_access = false) = 0; - - virtual void touch_file(const path& file_path, bool full_write_access = false) = 0; - - virtual void delete_file(const path& file_path) = 0; - - virtual void copy_file(const path& source_path, const path& destination_path, - bool replace_existing) = 0; - - virtual void rename_file(const path& source_path, const path& destination_path, - bool replace_existing, bool allow_copy) = 0; - -protected: - FILE* output() { return m_output; } - bool cleanoutput() const { return m_cleanoutput; } - static const char* write_operation_name(write_mode mode); - static const char* rename_operation_name(bool replace_existing, bool allow_copy); - -public: // mainly for derived class (but also used by helper classes like SafeHandle so - // public) - void print_operation(const char* operation, const path& target); - void print_operation(const char* operation, const path& source, const path& target); - void print_result(const char* operation, uint32_t result, - bool with_last_error = false, const char* opt_arg = nullptr, - bool hide_result = false); - void print_error(const char* operation, uint32_t result, bool with_last_error = false, - const char* opt_arg = nullptr); - void print_write_success(const void* data, std::size_t size, std::size_t written); - - uint32_t clean_attributes(uint32_t attr); - -private: - FILE* m_output; - bool m_cleanoutput = false; - path m_basepath; -}; diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.cpp b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.cpp deleted file mode 100644 index 34966b5..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.cpp +++ /dev/null @@ -1,488 +0,0 @@ - -#include "test_ntapi.h" -#include <cstdio> -#include <cstring> -#include <test_helpers.h> -#include <vector> - -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> -#include <Winternl.h> -#include <stdio.h> - -#include "test_ntdll_declarations.h" - -class TestNtApi::SafeHandle -{ -public: - SafeHandle(TestFileSystem* tfs, HANDLE handle = NULL) : m_handle(handle), m_tfs(tfs) - {} - SafeHandle(const SafeHandle&) = delete; - SafeHandle(SafeHandle&& other) : m_handle(other.m_handle), m_tfs(other.m_tfs) - { - other.m_handle = nullptr; - } - - operator HANDLE() { return m_handle; } - operator PHANDLE() { return &m_handle; } - - bool valid() const { return m_handle != NULL; } - - ~SafeHandle() - { - if (m_handle) { - NTSTATUS status = NtClose(m_handle); - if (m_tfs) - m_tfs->print_result("NtClose", status); - if (!NT_SUCCESS(status)) - if (m_tfs) - m_tfs->print_error("NtClose", status); - else - std::fprintf(stderr, "NtClose failed : 0x%lx", status); - m_handle = NULL; - } - } - -private: - HANDLE m_handle; - TestFileSystem* m_tfs; -}; - -const char* TestNtApi::id() -{ - return "Nt"; -} - -TestNtApi::path TestNtApi::real_path(const char* abs_or_rel_path) -{ - if (!abs_or_rel_path || !abs_or_rel_path[0]) - return path(); - - static constexpr char nt_path_prefix[] = "\\??\\"; - static constexpr wchar_t nt_path_prefix_w[] = L"\\??\\"; - - bool path_dos = strncmp(abs_or_rel_path, nt_path_prefix, strlen(nt_path_prefix)) == 0; - bool path_has_drive = abs_or_rel_path[1] == ':'; - bool path_unc = abs_or_rel_path[0] == '\\' && abs_or_rel_path[1] == '\\'; - bool path_absolute = path_has_drive || abs_or_rel_path[0] == '\\'; - - path result; - if (!path_dos) { - if (!path_unc) - result.assign(nt_path_prefix_w); - if (!path_absolute) - result /= current_directory(); - else if (!path_has_drive && !path_unc) - // if "absolute" path but without a drive letter (i.e. \windows) - // the take the drive from the current directory: (i.e. "C:") - result /= current_directory().root_name(); - } - - int result_size = 0; - for (auto r : result) - ++result_size; - - // now append abs_or_rel_path, handling ".." and "." properly: - path arp{abs_or_rel_path}; - int base_size = path_unc ? 3 : 4; - for (auto p : arp) { - if (p == "..") { - if (result_size > base_size) { // refuse to remove top level element (i.e. - // \??\C:\ which is 4 elements) - result.remove_filename(); - --result_size; - } - } else if (!p.empty() && p != ".") { - result /= p; - ++result_size; - } - } - - return result; -} - -TestNtApi::SafeHandle TestNtApi::open_directory(const path& directory_path, bool create, - bool allow_non_existence, long* pstatus) -{ - print_operation(create ? "Creating directory" : "Openning directory", directory_path); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, directory_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - SafeHandle dir(this); - IO_STATUS_BLOCK iosb; - NTSTATUS status = NtCreateFile( - dir, FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &attributes, &iosb, NULL, - FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - create ? FILE_OPEN_IF : FILE_OPEN, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); - - print_result("NtCreateFile", status); - - if (pstatus) - *pstatus = status; - if ((status == STATUS_OBJECT_NAME_NOT_FOUND || - status == STATUS_OBJECT_PATH_NOT_FOUND) && - allow_non_existence) - return NULL; - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtCreateFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtCreateFile", "bad iosb.Status", iosb.Status); - - return dir; -} - -TestFileSystem::FileInfoList TestNtApi::list_directory(const path& directory_path) -{ - SafeHandle dir = open_directory(directory_path, false); - - print_operation("Querying directory", directory_path); - - FileInfoList files; - while (true) { - char buf[4096]{0}; - IO_STATUS_BLOCK iosb; - - NTSTATUS status = - NtQueryDirectoryFile(dir, NULL, NULL, NULL, &iosb, buf, sizeof(buf), - MyFileBothDirectoryInformation, FALSE, NULL, FALSE); - print_result("NtQueryDirectoryFile", status); - - if (status == STATUS_NO_MORE_FILES) - break; - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtQueryDirectoryFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtQueryDirectoryFile", "bad iosb.Status", iosb.Status); - if (iosb.Information == 0) // This shouldn't happend unless the filename (not full - // path) is larger then sizeof(buf) - throw test::FuncFailed("NtQueryDirectoryFile", "buffer too small", - iosb.Information); - - PFILE_BOTH_DIR_INFORMATION info = reinterpret_cast<PFILE_BOTH_DIR_INFORMATION>(buf); - while (true) { - files.push_back(FileInformation( - std::wstring(info->FileName, - info->FileNameLength / sizeof(info->FileName[0])), - clean_attributes(info->FileAttributes), info->EndOfFile.QuadPart)); - if (info->NextEntryOffset) - info = reinterpret_cast<PFILE_BOTH_DIR_INFORMATION>( - reinterpret_cast<char*>(info) + info->NextEntryOffset); - else - break; - } - } - - return files; -} - -void TestNtApi::create_path(const path& directory_path) -{ - // sanity and guaranteed recursion end: - if (!directory_path.has_relative_path()) - throw std::runtime_error("Refusing to create non-existing top level path"); - - // if directory already exists all is good - NTSTATUS status; - if (open_directory(directory_path, false, true, &status).valid()) - return; - - if (status != STATUS_OBJECT_NAME_NOT_FOUND) // STATUS_OBJECT_NAME_NOT_FOUND means - // parent directory already exists - create_path(directory_path - .parent_path()); // otherwise create parent directory (recursively) - - open_directory(directory_path, true); -} - -void TestNtApi::read_file(const path& file_path) -{ - print_operation("Reading file", file_path); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, file_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - SafeHandle file(this); - IO_STATUS_BLOCK iosb; - NTSTATUS status = NtOpenFile(file, GENERIC_READ | SYNCHRONIZE, &attributes, &iosb, - FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); - print_result("NtOpenFile", status); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtOpenFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtOpenFile", "bad iosb.Status", iosb.Status); - - uint32_t total = 0; - bool ends_with_newline = true; - bool pending_prefix = true; - while (true) { - char buf[4096]; - - memset(&iosb, 0, sizeof(iosb)); - status = NtReadFile(file, NULL, NULL, NULL, &iosb, buf, sizeof(buf), NULL, NULL); - print_result("NtReadFile", status); - if (status == STATUS_END_OF_FILE) - break; - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtReadFile", status); - - total += iosb.Information; - char* begin = buf; - char* end = begin + iosb.Information; - while (begin != end) { - if (pending_prefix) { - if (output()) - fwrite(FILE_CONTENTS_PRINT_PREFIX, 1, strlen(FILE_CONTENTS_PRINT_PREFIX), - output()); - pending_prefix = false; - } - bool skip_newline = false; - char* print_end = reinterpret_cast<char*>(std::memchr(begin, '\n', end - begin)); - if (print_end) { - pending_prefix = true; - if (print_end > begin && *(print_end - 1) == '\r') { - // convert \r\n => \n: - *(print_end - 1) = '\n'; - skip_newline = true; - } else // only a '\n' so just print it - ++print_end; - } else { - print_end = end; - if (print_end > begin && *(print_end - 1) == '\r') { - // buffer ends with \r so skip it under the hope it will be followed with a \n - --print_end; - skip_newline = true; - } - } - if (output()) - fwrite(begin, 1, print_end - begin, output()); - ends_with_newline = print_end > begin && *(print_end - 1) == '\n'; - begin = print_end; - if (skip_newline) - ++begin; - } - if (output() && !ends_with_newline) { - fwrite("\n", 1, 1, output()); - ends_with_newline = true; - } - } - if (output()) { - fprintf(output(), "# Successfully read %u bytes.\n", total); - } -} - -void TestNtApi::write_file(const path& file_path, const void* data, std::size_t size, - bool add_new_line, write_mode mode, bool rw_access) -{ - print_operation(write_operation_name(mode), file_path); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, file_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - ACCESS_MASK access = GENERIC_WRITE | SYNCHRONIZE; - ULONG disposition = FILE_OPEN; - switch (mode) { - case write_mode::truncate: - disposition = FILE_OVERWRITE; - break; - case write_mode::create: - disposition = FILE_CREATE; - break; - case write_mode::overwrite: - disposition = FILE_SUPERSEDE; - break; - case write_mode::opencreate: - disposition = FILE_OPEN_IF; - break; - case write_mode::append: - disposition = FILE_OPEN_IF; - access = FILE_APPEND_DATA | SYNCHRONIZE; - break; - } - if (rw_access) - access |= GENERIC_READ; - - SafeHandle file(this); - IO_STATUS_BLOCK iosb; - NTSTATUS status = - NtCreateFile(file, access, &attributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, - disposition, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); - print_result("NtCreateFile", status); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtCreateFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtCreateFile", "bad iosb.Status", iosb.Status); - - if (mode == write_mode::manual_truncate) { - FILE_END_OF_FILE_INFORMATION eofinfo{0}; - status = NtSetInformationFile(file, &iosb, &eofinfo, sizeof(eofinfo), - MyFileEndOfFileInformation); - print_result("NtSetInformationFile", status, false, "EOF"); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtSetInformationFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtSetInformationFile", "bad iosb.Status", iosb.Status); - } - - // finally write the data: - size_t total = 0; - - if (data) { - status = NtWriteFile(file, NULL, NULL, NULL, &iosb, const_cast<void*>(data), - static_cast<ULONG>(size), NULL, NULL); - print_result("NtWriteFile", status); - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtWriteFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtWriteFile", "bad iosb.Status", iosb.Status); - total += iosb.Information; - - if (add_new_line) { - char buffer[] = "\r\n"; - status = NtWriteFile(file, NULL, NULL, NULL, &iosb, buffer, 2, NULL, NULL); - print_result("NtWriteFile", status); - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtWriteFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtWriteFile", "bad iosb.Status", iosb.Status); - total += iosb.Information; - } - } - - print_write_success(data, size, total); -} - -void TestNtApi::touch_file(const path& file_path, bool full_write_access) -{ - print_operation("Touching file", file_path); - - SYSTEMTIME st; - GetSystemTime(&st); - FILETIME ft; - if (!SystemTimeToFileTime(&st, &ft)) - test::throw_testWinFuncFailed("SystemTimeToFileTime"); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, file_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - SafeHandle file(this); - IO_STATUS_BLOCK iosb; - auto share_all = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - auto access = - (full_write_access ? GENERIC_WRITE : FILE_WRITE_ATTRIBUTES) | SYNCHRONIZE; - NTSTATUS status = - NtCreateFile(file, access, &attributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, - share_all, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); - print_result("NtCreateFile", status); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtCreateFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtCreateFile", "bad iosb.Status", iosb.Status); - - FILE_BASIC_INFORMATION basicinfo{0}; - basicinfo.LastWriteTime.LowPart = ft.dwLowDateTime; - basicinfo.LastWriteTime.HighPart = ft.dwHighDateTime; - status = NtSetInformationFile(file, &iosb, &basicinfo, sizeof(basicinfo), - MyFileBasicInformation); - print_result("NtSetInformationFile", status, false, "Basic"); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtSetInformationFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtSetInformationFile", "bad iosb.Status", iosb.Status); -} - -void TestNtApi::delete_file(const path& file_path) -{ - print_operation("Deleting file", file_path); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, file_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - NTSTATUS status = NtDeleteFile(&attributes); - print_result("NtCreateFile", status); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtDeleteFile", status); -} - -void TestNtApi::copy_file(const path& source_path, const path& destination_path, - bool replace_existing) -{ - throw test::FuncFailed("copy_file", "ntapi does not support file copy"); -} - -void TestNtApi::rename_file(const path& source_path, const path& destination_path, - bool replace_existing, bool allow_copy) -{ - if (allow_copy) - throw test::FuncFailed("rename_file", "ntapi does not support file move"); - - print_operation(rename_operation_name(replace_existing, allow_copy), source_path, - destination_path); - - UNICODE_STRING unicode_path; - RtlInitUnicodeString(&unicode_path, source_path.c_str()); - - OBJECT_ATTRIBUTES attributes; - InitializeObjectAttributes(&attributes, &unicode_path, OBJ_CASE_INSENSITIVE, NULL, - NULL); - - SafeHandle file(this); - IO_STATUS_BLOCK iosb; - NTSTATUS status = NtCreateFile(file, FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, - &attributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); - print_result("NtCreateFile", status); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtCreateFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtCreateFile", "bad iosb.Status", iosb.Status); - - bool dest_full_path = source_path.parent_path() != destination_path.parent_path(); - std::wstring dest = dest_full_path ? destination_path : destination_path.filename(); - std::vector<char> buf(sizeof(FILE_RENAME_INFORMATION) + - sizeof(wchar_t) * dest.length()); - FILE_RENAME_INFORMATION* rename = - reinterpret_cast<FILE_RENAME_INFORMATION*>(buf.data()); - rename->ReplaceIfExists = replace_existing ? TRUE : FALSE; - rename->FileNameLength = sizeof(wchar_t) * dest.length(); - memcpy(&rename->FileName[0], dest.data(), sizeof(wchar_t) * dest.length()); - - status = - NtSetInformationFile(file, &iosb, rename, buf.size(), MyFileRenameInformation); - print_result("NtSetInformationFile", status, false, - dest_full_path ? "rename full path" : "rename filename"); - - if (!NT_SUCCESS(status)) - throw test::FuncFailed("NtSetInformationFile", status); - if (!NT_SUCCESS(iosb.Status)) - throw test::FuncFailed("NtSetInformationFile", "bad iosb.Status", iosb.Status); -} diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.h b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.h deleted file mode 100644 index fc6434e..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntapi.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "test_filesystem.h" - -class TestNtApi : public TestFileSystem -{ -public: - TestNtApi(FILE* output) : TestFileSystem(output) {} - - path real_path(const char* abs_or_rel_path) override; - - FileInfoList list_directory(const path& directory_path) override; - - void create_path(const path& directory_path) override; - - void read_file(const path& file_path) override; - - void write_file(const path& file_path, const void* data, std::size_t size, - bool add_new_line, write_mode mode, bool rw_access = false) override; - - void touch_file(const path& file_path, bool full_write_access = false) override; - - void delete_file(const path& file_path) override; - - void copy_file(const path& source_path, const path& destination_path, - bool replace_existing) override; - - void rename_file(const path& source_path, const path& destination_path, - bool replace_existing, bool allow_copy) override; - - const char* id() override; - -private: - class SafeHandle; - - SafeHandle open_directory(const path& directory_path, bool create, - bool allow_non_existence = false, long* pstatus = nullptr); -}; diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntdll_declarations.h b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntdll_declarations.h deleted file mode 100644 index e3799ac..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_ntdll_declarations.h +++ /dev/null @@ -1,157 +0,0 @@ -#pragma once - -#define STATUS_NO_MORE_FILES 0x80000006 -#define STATUS_END_OF_FILE 0xC0000011 -#define STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 -#define STATUS_OBJECT_PATH_NOT_FOUND 0xC000003A - -#define FILE_WRITE_TO_END_OF_FILE 0xffffffff -#define FILE_USE_FILE_POINTER_POSITION 0xfffffffe - -#define InitializeObjectAttributes(p, n, a, r, s) \ - { \ - (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ - (p)->RootDirectory = r; \ - (p)->Attributes = a; \ - (p)->ObjectName = n; \ - (p)->SecurityDescriptor = s; \ - (p)->SecurityQualityOfService = NULL; \ - } - -// winternl.h of course defines a joke FILE_INFORMATION_CLASS (why?!) -// so added MY_ to avoid name collision but this is FILE_INFORMATION_CLASS - -typedef enum _MY_FILE_INFORMATION_CLASS -{ - MyFileDirectoryInformation = 1, - MyFileFullDirectoryInformation, - MyFileBothDirectoryInformation, - MyFileBasicInformation, - MyFileStandardInformation, - MyFileInternalInformation, - MyFileEaInformation, - MyFileAccessInformation, - MyFileNameInformation, - MyFileRenameInformation, - MyFileLinkInformation, - MyFileNamesInformation, - MyFileDispositionInformation, - MyFilePositionInformation, - MyFileFullEaInformation, - MyFileModeInformation, - MyFileAlignmentInformation, - MyFileAllInformation, - MyFileAllocationInformation, - MyFileEndOfFileInformation, - MyFileAlternateNameInformation, - MyFileStreamInformation, - MyFilePipeInformation, - MyFilePipeLocalInformation, - MyFilePipeRemoteInformation, - MyFileMailslotQueryInformation, - MyFileMailslotSetInformation, - MyFileCompressionInformation, - MyFileObjectIdInformation, - MyFileCompletionInformation, - MyFileMoveClusterInformation, - MyFileQuotaInformation, - MyFileReparsePointInformation, - MyFileNetworkOpenInformation, - MyFileAttributeTagInformation, - MyFileTrackingInformation, - MyFileIdBothDirectoryInformation, - MyFileIdFullDirectoryInformation, - MyFileValidDataLengthInformation, - MyFileShortNameInformation, - MyFileIoCompletionNotificationInformation, - MyFileIoStatusBlockRangeInformation, - MyFileIoPriorityHintInformation, - MyFileSfioReserveInformation, - MyFileSfioVolumeInformation, - MyFileHardLinkInformation, - MyFileProcessIdsUsingFileInformation, - MyFileNormalizedNameInformation, - MyFileNetworkPhysicalNameInformation, - MyFileIdGlobalTxDirectoryInformation, - MyFileIsRemoteDeviceInformation, - MyFileUnusedInformation, - MyFileNumaNodeInformation, - MyFileStandardLinkInformation, - MyFileRemoteProtocolInformation, - MyFileRenameInformationBypassAccessCheck, - MyFileLinkInformationBypassAccessCheck, - MyFileVolumeNameInformation, - MyFileIdInformation, - MyFileIdExtdDirectoryInformation, - MyFileReplaceCompletionInformation, - MyFileHardLinkFullIdInformation, - MyFileIdExtdBothDirectoryInformation, - MyFileDispositionInformationEx, - MyFileRenameInformationEx, - MyFileRenameInformationExBypassAccessCheck, - MyFileMaximumInformation -} MY_FILE_INFORMATION_CLASS, - *PMY_FILE_INFORMATION_CLASS; - -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_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_END_OF_FILE_INFORMATION -{ - LARGE_INTEGER EndOfFile; -} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; - -typedef struct _FILE_RENAME_INFORMATION -{ - BOOLEAN ReplaceIfExists; - HANDLE RootDirectory; - ULONG FileNameLength; - WCHAR FileName[1]; -} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; - -extern "C" __kernel_entry NTSTATUS NTAPI NtQueryDirectoryFile( - IN HANDLE FileHandle, IN HANDLE Event, IN PIO_APC_ROUTINE ApcRoutine, - IN PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, - IN ULONG Length, IN MY_FILE_INFORMATION_CLASS FileInformationClass, - IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName, IN BOOLEAN RestartScan); - -extern "C" __kernel_entry NTSTATUS NTAPI -NtReadFile(IN HANDLE FileHandle, IN HANDLE Event, IN PIO_APC_ROUTINE ApcRoutine, - IN PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, - IN ULONG Length, IN PLARGE_INTEGER ByteOffset, IN PULONG Key); - -extern "C" __kernel_entry NTSTATUS NTAPI -NtWriteFile(IN HANDLE FileHandle, IN HANDLE Event, IN PIO_APC_ROUTINE ApcRoutine, - IN PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, - IN ULONG Length, IN PLARGE_INTEGER ByteOffset, IN PULONG Key); - -extern "C" __kernel_entry NTSTATUS NTAPI NtSetInformationFile( - IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, - IN ULONG Length, IN MY_FILE_INFORMATION_CLASS FileInformationClass); - -extern "C" __kernel_entry NTSTATUS NTAPI -NtDeleteFile(IN POBJECT_ATTRIBUTES ObjectAttributes); diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.cpp b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.cpp deleted file mode 100644 index 372a6f3..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.cpp +++ /dev/null @@ -1,370 +0,0 @@ - -#include "test_w32api.h" -#include <cstdio> -#include <cstring> -#include <test_helpers.h> - -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> - -using test::throw_testWinFuncFailed; - -class TestW32Api::SafeHandle -{ -public: - SafeHandle(TestFileSystem* tfs, HANDLE handle = NULL) : m_handle(handle), m_tfs(tfs) - {} - SafeHandle(const SafeHandle&) = delete; - SafeHandle(SafeHandle&& other) : m_handle(other.m_handle), m_tfs(other.m_tfs) - { - other.m_handle = nullptr; - } - - operator HANDLE() { return m_handle; } - operator PHANDLE() { return &m_handle; } - uint32_t result_for_print() - { - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(m_handle)); - } - - bool valid() const { return m_handle != INVALID_HANDLE_VALUE; } - - ~SafeHandle() - { - if (m_handle != INVALID_HANDLE_VALUE) { - BOOL res = CloseHandle(m_handle); - if (m_tfs) - m_tfs->print_result("CloseHandle", res, true); - if (!res) - if (m_tfs) - m_tfs->print_error("CloseHandle", res, true); - else - std::fprintf(stderr, "CloseHandle failed : %d", GetLastError()); - m_handle = NULL; - } - } - -private: - HANDLE m_handle; - TestFileSystem* m_tfs; -}; - -class TestW32Api::SafeFindHandle -{ -public: - SafeFindHandle(TestFileSystem* tfs, HANDLE handle = NULL) - : m_handle(handle), m_tfs(tfs) - {} - SafeFindHandle(const SafeFindHandle&) = delete; - SafeFindHandle(SafeFindHandle&& other) : m_handle(other.m_handle), m_tfs(other.m_tfs) - { - other.m_handle = nullptr; - } - - operator HANDLE() { return m_handle; } - operator PHANDLE() { return &m_handle; } - uint32_t result_for_print() - { - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(m_handle)); - } - - bool valid() const { return m_handle != INVALID_HANDLE_VALUE; } - - ~SafeFindHandle() - { - if (m_handle != INVALID_HANDLE_VALUE) { - BOOL res = FindClose(m_handle); - if (m_tfs) - m_tfs->print_result("CloseHandle", res, true); - if (!res) - if (m_tfs) - m_tfs->print_error("CloseHandle", res, true); - else - std::fprintf(stderr, "CloseHandle failed : %d", GetLastError()); - m_handle = NULL; - } - } - -private: - HANDLE m_handle; - TestFileSystem* m_tfs; -}; - -const char* TestW32Api::id() -{ - return "W32"; -} - -TestW32Api::path TestW32Api::real_path(const char* abs_or_rel_path) -{ - if (!abs_or_rel_path || !abs_or_rel_path[0]) - return path(); - - char buf[1024]; - size_t res = GetFullPathNameA(abs_or_rel_path, _countof(buf), buf, NULL); - if (!res || res >= _countof(buf)) - throw_testWinFuncFailed("GetFullPathNameA", res); - return buf; -} - -TestFileSystem::FileInfoList TestW32Api::list_directory(const path& directory_path) -{ - print_operation("Querying directory", directory_path); - - WIN32_FIND_DATA fd; - SafeFindHandle find(this, FindFirstFileW((directory_path / L"*").c_str(), &fd)); - print_result("FindFirstFileW", 0, true, nullptr, true); - if (!find.valid()) - throw_testWinFuncFailed("FindFirstFileW"); - - FileInfoList files; - while (true) { - files.push_back( - FileInformation(fd.cFileName, clean_attributes(fd.dwFileAttributes), - fd.nFileSizeHigh * (MAXDWORD + 1) + fd.nFileSizeLow)); - BOOL res = FindNextFileW(find, &fd); - print_result("FindNextFileW", res, true); - if (!res) - break; - } - - return files; -} - -void TestW32Api::create_path(const path& directory_path) -{ - // sanity and guaranteed recursion end: - if (!directory_path.has_relative_path()) - throw std::runtime_error("Refusing to create non-existing top level path"); - - print_operation("Checking directory", directory_path); - - DWORD attr = GetFileAttributesW(directory_path.c_str()); - DWORD err = GetLastError(); - print_result("GetFileAttributesW", clean_attributes(attr), true); - if (attr != INVALID_FILE_ATTRIBUTES) { - if (attr & FILE_ATTRIBUTE_DIRECTORY) - return; // if directory already exists all is good - else - throw std::runtime_error("path exists but not a directory"); - } - if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) - throw_testWinFuncFailed("GetFileAttributesW"); - - if (err != ERROR_FILE_NOT_FOUND) // ERROR_FILE_NOT_FOUND means parent directory - // already exists - create_path(directory_path - .parent_path()); // otherwise create parent directory (recursively) - - print_operation("Creating directory", directory_path); - - BOOL res = CreateDirectoryW(directory_path.c_str(), NULL); - print_result("CreateDirectoryW", res, true); - if (!res) - throw_testWinFuncFailed("CreateDirectoryW"); -} - -void TestW32Api::read_file(const path& file_path) -{ - print_operation("Reading file", file_path); - - SafeHandle file(this, CreateFileW(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); - print_result("CreateFileW", 0, true, nullptr, true); - if (!file.valid()) - throw_testWinFuncFailed("CreateFileW"); - - uint32_t total = 0; - bool ends_with_newline = true; - bool pending_prefix = true; - while (true) { - char buf[4096]; - - DWORD read = 0; - BOOL res = ReadFile(file, buf, sizeof(buf), &read, NULL); - print_result("ReadFile", res, true); - if (!res) - throw_testWinFuncFailed("ReadFile"); - if (!read) // eof - break; - - total += read; - char* begin = buf; - char* end = begin + read; - while (begin != end) { - if (pending_prefix) { - if (output()) - fwrite(FILE_CONTENTS_PRINT_PREFIX, 1, strlen(FILE_CONTENTS_PRINT_PREFIX), - output()); - pending_prefix = false; - } - bool skip_newline = false; - char* print_end = reinterpret_cast<char*>(std::memchr(begin, '\n', end - begin)); - if (print_end) { - pending_prefix = true; - if (print_end > begin && *(print_end - 1) == '\r') { - // convert \r\n => \n: - *(print_end - 1) = '\n'; - skip_newline = true; - } else // only a '\n' so just print it - ++print_end; - } else { - print_end = end; - if (print_end > begin && *(print_end - 1) == '\r') { - // buffer ends with \r so skip it under the hope it will be followed with a \n - --print_end; - skip_newline = true; - } - } - if (output()) - fwrite(begin, 1, print_end - begin, output()); - ends_with_newline = print_end > begin && *(print_end - 1) == '\n'; - begin = print_end; - if (skip_newline) - ++begin; - } - if (output() && !ends_with_newline) { - fwrite("\n", 1, 1, output()); - ends_with_newline = true; - } - } - if (output()) { - fprintf(output(), "# Successfully read %u bytes.\n", total); - } -} - -void TestW32Api::write_file(const path& file_path, const void* data, std::size_t size, - bool add_new_line, write_mode mode, bool rw_access) -{ - print_operation(write_operation_name(mode), file_path); - - ACCESS_MASK access = GENERIC_WRITE; - DWORD disposition = OPEN_EXISTING; - switch (mode) { - case write_mode::truncate: - disposition = TRUNCATE_EXISTING; - break; - case write_mode::create: - disposition = CREATE_NEW; - break; - case write_mode::overwrite: - disposition = CREATE_ALWAYS; - break; - case write_mode::opencreate: - disposition = OPEN_ALWAYS; - break; - case write_mode::append: - disposition = OPEN_ALWAYS; - access = FILE_APPEND_DATA; - break; - } - if (rw_access) - access |= GENERIC_READ; - - SafeHandle file(this, CreateFile(file_path.c_str(), access, 0, NULL, disposition, - FILE_ATTRIBUTE_NORMAL, NULL)); - print_result("CreateFileW", 0, true, nullptr, true); - if (!file.valid()) - throw_testWinFuncFailed("CreateFile"); - - if (mode == write_mode::manual_truncate) { - BOOL res = SetEndOfFile(file); - print_result("SetEndOfFile", res, true); - if (!res) - throw_testWinFuncFailed("SetEndOfFile"); - } - - if (mode == write_mode::append) { - DWORD res = SetFilePointer(file, 0, NULL, FILE_END); - print_result("SetFilePointer(FILE_END)", res, true); - if (res == INVALID_SET_FILE_POINTER) - throw_testWinFuncFailed("SetEndOfFile"); - } - - size_t total = 0; - - if (data) { - // finally write the data: - DWORD written = 0; - BOOL res = WriteFile(file, data, static_cast<DWORD>(size), &written, NULL); - print_result("WriteFile", written, true); - if (!res) - throw_testWinFuncFailed("WriteFile"); - total += written; - - if (add_new_line) { - res = WriteFile(file, "\r\n", 2, &written, NULL); - print_result("WriteFile", written, true, "<new line>"); - if (!res) - throw_testWinFuncFailed("WriteFile"); - total += written; - } - } - - print_write_success(data, size, total); -} - -void TestW32Api::touch_file(const path& file_path, bool full_write_access) -{ - print_operation("Touching file", file_path); - - SYSTEMTIME st; - GetSystemTime(&st); - FILETIME ft; - if (!SystemTimeToFileTime(&st, &ft)) - throw_testWinFuncFailed("SystemTimeToFileTime"); - - auto share_all = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - auto access = full_write_access ? GENERIC_WRITE : FILE_WRITE_ATTRIBUTES; - SafeHandle file(this, CreateFile(file_path.c_str(), access, share_all, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); - print_result("CreateFileW", 0, true, nullptr, true); - if (!file.valid()) - throw_testWinFuncFailed("CreateFile"); - - BOOL res = SetFileTime(file, nullptr, nullptr, &ft); - print_result("SetFileTime", res, true); - if (!res) - throw_testWinFuncFailed("SetFileTime"); -} - -void TestW32Api::delete_file(const path& file_path) -{ - print_operation("Deleting file", file_path); - - BOOL res = DeleteFileW(file_path.c_str()); - print_result("DeleteFileW", res, true); - if (!res) - throw_testWinFuncFailed("DeleteFileW"); -} - -void TestW32Api::copy_file(const path& source_path, const path& destination_path, - bool replace_existing) -{ - print_operation(replace_existing ? "Copy file over" : "Copy file", source_path, - destination_path); - - BOOL res = - CopyFileW(source_path.c_str(), destination_path.c_str(), !replace_existing); - print_result("CopyFileW", res, true); - if (!res) - throw_testWinFuncFailed("CopyFileW"); -} - -void TestW32Api::rename_file(const path& source_path, const path& destination_path, - bool replace_existing, bool allow_copy) -{ - print_operation(rename_operation_name(replace_existing, allow_copy), source_path, - destination_path); - - DWORD flags = 0; - if (replace_existing) - flags |= MOVEFILE_REPLACE_EXISTING; - if (allow_copy) - flags |= MOVEFILE_COPY_ALLOWED; - - BOOL res = MoveFileExW(source_path.c_str(), destination_path.c_str(), flags); - print_result("MoveFileExW", res, true); - if (!res) - throw_testWinFuncFailed("MoveFileExW"); -} diff --git a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.h b/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.h deleted file mode 100644 index cab8e54..0000000 --- a/libs/usvfs/test/usvfs_test_runner/test_file_operations/test_w32api.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "test_filesystem.h" - -class TestW32Api : public TestFileSystem -{ -public: - TestW32Api(FILE* output) : TestFileSystem(output) {} - - path real_path(const char* abs_or_rel_path) override; - - FileInfoList list_directory(const path& directory_path) override; - - void create_path(const path& directory_path) override; - - void read_file(const path& file_path) override; - - void write_file(const path& file_path, const void* data, std::size_t size, - bool add_new_line, write_mode mode, bool rw_access = false) override; - - void touch_file(const path& file_path, bool full_write_access = false) override; - - void delete_file(const path& file_path) override; - - void copy_file(const path& source_path, const path& destination_path, - bool replace_existing) override; - - void rename_file(const path& source_path, const path& destination_path, - bool replace_existing, bool allow_copy) override; - - const char* id() override; - -private: - class SafeHandle; - class SafeFindHandle; -}; diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.cpp b/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.cpp deleted file mode 100644 index c5fb3a1..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.cpp +++ /dev/null @@ -1,402 +0,0 @@ -#include "usvfs_basic_test.h" - -const char* usvfs_basic_test::scenario_name() -{ - return SCENARIO_NAME; -} - -bool usvfs_basic_test::scenario_run() -{ - // Note: For regression purposes we don't really need to verify the results of most - // our operations as the usvfs_test_base postmortem_check will verify the final state. - // We still also verify the results here because: - // A. In some cases a later step may change the results. - // B. It is easier to understand and maintain the test when the important checks are - // together with - // their related operations. - // C. For open issues the verifications here serve as a "documentation" of the issue - // (i.e. not having - // proper copy_on_write, etc.). - - // Cases not covered by the test: - // - When a "temporary" virtualized directory is created (meaning the directory is - // created only to "shadow" - // a real directory) and is then emptied. This needs to be tested within the same - // prcoess and across different processes. - - ops_list(LR"(.)", true, true); - - // test proper path creation under overwrite when a virtualized folder is written to: - - verify_source_existance(LR"(overwrite\mfolder1)", false); - verify_source_existance(LR"(overwrite\mfolder2)", false); - verify_source_existance(LR"(overwrite\mfolder3)", false); - verify_source_existance(LR"(overwrite\mfolder4)", false); - - ops_overwrite(LR"(mfolder1\newfolder1\newfile1.txt)", - R"(newfile1.txt nonrecursive overwrite subfolder)", false); - verify_source_existance(LR"(overwrite\mfolder1\newfolder1\newfile1.txt)"); - ops_overwrite(LR"(mfolder1\newfile1.txt)", R"(newfile1.txt nonrecursive overwrite)", - false); - ops_read(LR"(mfolder1\newfile1.txt)"); - - ops_overwrite(LR"(mfolder2\newfile2.txt)", R"(newfile2.txt recursive overwrite)", - true); - ops_read(LR"(mfolder2\newfile2.txt)"); - verify_source_contents(LR"(overwrite\mfolder2\newfile2.txt)", - R"(newfile2.txt recursive overwrite)"); - ops_overwrite(LR"(mfolder2\newfile2.txt\fail)", - R"(newfile2.txt is a file so folder creation should fail)", true, - false); - verify_source_contents(LR"(overwrite\mfolder2\newfile2.txt)", - R"(newfile2.txt recursive overwrite)"); - ops_overwrite(LR"(mfolder2\mfile.txt\fail)", - R"(mfile.txt is a file so folder creation should fail)", true, false); - verify_source_existance(LR"(overwrite\mfolder2\mfile.txt)", false); - - ops_overwrite(LR"(mfolder3\newfolder3\newfile3.txt)", - R"(newfile3.txt recursive overwrite)", true); - ops_read(LR"(mfolder3\newfolder3\newfile3.txt)"); - verify_source_contents(LR"(overwrite\mfolder3\newfolder3\newfile3.txt)", - R"(newfile3.txt recursive overwrite)"); - // repeat mfolder3\newfolder3 test as that folder now exists in overwrite and that - // changes things - ops_overwrite(LR"(mfolder3\newfolder3\newfile3e.txt)", - R"(newfile3e.txt recursive overwrite)", true); - ops_read(LR"(mfolder3\newfolder3\newfile3e.txt)"); - verify_source_contents(LR"(overwrite\mfolder3\newfolder3\newfile3e.txt)", - R"(newfile3e.txt recursive overwrite)"); - - ops_overwrite(LR"(mfolder4\newfolder4\d\e\e\p\newfile4.txt)", - R"(newfile4.txt recursive overwrite)", true); - ops_read(LR"(mfolder4\newfolder4\d\e\e\p\newfile4.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4.txt)", - R"(newfile4.txt recursive overwrite)"); - // repeat mfolder4\newfolder4\d\e\e\p test as that folder now exists in overwrite and - // that changes things - ops_overwrite(LR"(mfolder4\newfolder4\d\e\e\p\newfile4e.txt)", - R"(newfile4e.txt recursive overwrite)", true); - ops_read(LR"(mfolder4\newfolder4\d\e\e\p\newfile4e.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4e.txt)", - R"(newfile4e.txt recursive overwrite)"); - // and finally verify also non-recursive works - ops_overwrite(LR"(mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)", - R"(newfile4enr.txt nonrecursive overwrite)", false); - ops_read(LR"(mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)", - R"(newfile4enr.txt nonrecursive overwrite)"); - // finally check an intermediate folder: - ops_overwrite(LR"(mfolder4\newfolder4\d\e\epnewfile4r.txt)", - R"(epnewfile4r.txt recursive overwrite)", true); - ops_read(LR"(mfolder4\newfolder4\d\e\epnewfile4r.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\epnewfile4r.txt)", - R"(epnewfile4r.txt recursive overwrite)"); - ops_overwrite(LR"(mfolder4\newfolder4\d\e\epnewfile4.txt)", - R"(epnewfile4.txt nonrecursive overwrite)", false); - ops_read(LR"(mfolder4\newfolder4\d\e\epnewfile4.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\epnewfile4.txt)", - R"(epnewfile4.txt nonrecursive overwrite)"); - - verify_mount_existance(LR"(rfolder\rcopyme4.txt)"); - verify_source_existance(LR"(overwrite\mfolder4\newfolder4p)", false); - ops_copy(LR"(rfolder\rcopyme4.txt)", LR"(mfolder4\newfolder4p\rcopyme4.txt)", true); - verify_source_existance(LR"(overwrite\mfolder4\newfolder4p\rcopyme4.txt)", true); - verify_source_existance(LR"(mod4\mfolder4\mfile.txt)"); - verify_source_existance(LR"(overwrite\mfolder4\mfile.txt)", false); - ops_copy(LR"(rfolder\rcopyme4.txt)", LR"(mfolder4\rcopyme4.txt)", false); - verify_source_existance(LR"(overwrite\mfolder4\rcopyme4.txt)"); - - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4.txt)", - R"(newfile4.txt recursive overwrite)"); - // repeat mfolder4\newfolder4\d\e\e\p test as that folder now exists in overwrite and - // that changes things - ops_overwrite(LR"(mfolder4\newfolder4\d\e\e\p\newfile4e.txt)", - R"(newfile4e.txt recursive overwrite)", true); - ops_read(LR"(mfolder4\newfolder4\d\e\e\p\newfile4e.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4e.txt)", - R"(newfile4e.txt recursive overwrite)"); - // and finally verify also non-recursive works - ops_overwrite(LR"(mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)", - R"(newfile4enr.txt nonrecursive overwrite)", false); - ops_read(LR"(mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\e\p\newfile4enr.txt)", - R"(newfile4enr.txt nonrecursive overwrite)"); - // finally check an intermediate folder: - ops_overwrite(LR"(mfolder4\newfolder4\d\e\epnewfile4r.txt)", - R"(epnewfile4r.txt recursive overwrite)", true); - ops_read(LR"(mfolder4\newfolder4\d\e\epnewfile4r.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\epnewfile4r.txt)", - R"(epnewfile4r.txt recursive overwrite)"); - ops_overwrite(LR"(mfolder4\newfolder4\d\e\epnewfile4.txt)", - R"(epnewfile4.txt nonrecursive overwrite)", false); - ops_read(LR"(mfolder4\newfolder4\d\e\epnewfile4.txt)"); - verify_source_contents(LR"(overwrite\mfolder4\newfolder4\d\e\epnewfile4.txt)", - R"(epnewfile4.txt nonrecursive overwrite)"); - - // test copy on write/delete against source "mod": - - ops_touch(LR"(root0.txt)"); - verify_source_existance(LR"(overwrite\root0.txt)", false); - ops_touch(LR"(root1.txt)"); - verify_source_existance(LR"(overwrite\root1.txt)", false); - ops_touch(LR"(root2.txt)"); - verify_source_existance(LR"(overwrite\root1.txt)", false); - ops_touch(LR"(mod1.txt)"); - verify_source_existance(LR"(overwrite\mod1.txt)", false); - ops_touch(LR"(mfolder1\mfile.txt)"); - verify_source_existance(LR"(overwrite\mfolder1\mfile.txt)", false); - - ops_touch(LR"(root0w.txt)", true); - verify_source_existance(LR"(overwrite\root0w.txt)", false); - ops_touch(LR"(root1w.txt)", true); - verify_source_existance(LR"(overwrite\root1w.txt)", false); - ops_touch(LR"(root2w.txt)", true); - verify_source_existance(LR"(overwrite\root1w.txt)", false); - ops_touch(LR"(mod1w.txt)", true); - verify_source_existance(LR"(overwrite\mod1w.txt)", false); - ops_touch(LR"(mfolder1\mfilew.txt)", true); - verify_source_existance(LR"(overwrite\mfolder1\mfilew.txt)", false); - - { - const auto& old_contents = source_contents(LR"(mod4\mfolder4\mfileoverwrite.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfileoverwrite.txt)", - old_contents.c_str()); - ops_overwrite(LR"(mfolder4\mfileoverwrite.txt)", - R"(mfolder4\mfileoverwrite.txt overwrite)", false); - ops_read(LR"(mfolder4\mfileoverwrite.txt)"); - if (bool protect_virtualized = false) { - verify_source_contents(LR"(mod4\mfolder4\mfileoverwrite.txt)", - old_contents.c_str()); - verify_source_contents(LR"(overwrite\mfolder4\mfileoverwrite.txt)", - R"(mfolder4\mfileoverwrite.txt overwrite)"); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfileoverwrite.txt)", - R"(mfolder4\mfileoverwrite.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfileoverwrite.txt)", false); - } - } - - { - const auto& old_contents = source_contents(LR"(mod4\mfolder4\mfilerewrite.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfilerewrite.txt)", old_contents.c_str()); - ops_rewrite(LR"(mfolder4\mfilerewrite.txt)", - R"(mfolder4\mfilerewrite.txt rewrite)"); - ops_read(LR"(mfolder4\mfilerewrite.txt)"); - if (auto copy_on_readwrite_implemented = false) { - verify_source_contents(LR"(mod4\mfolder4\mfilerewrite.txt)", - old_contents.c_str()); - verify_source_contents(LR"(overwrite\mfolder4\mfilerewrite.txt)", - R"(mfolder4\mfilerewrite.txt rewrite)"); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfilerewrite.txt)", - R"(mfolder4\mfilerewrite.txt rewrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfilerewrite.txt)", false); - } - } - - { - const auto& old_contents = source_contents(LR"(mod4\mfolder4\mfilemoveover.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfilemoveover.txt)", old_contents.c_str()); - ops_overwrite(LR"(mfolder4\temp_mfilemoveover.txt)", - R"(mfolder4\mfilemoveover.txt overwrite)", false); - verify_source_contents(LR"(overwrite\mfolder4\temp_mfilemoveover.txt)", - R"(mfolder4\mfilemoveover.txt overwrite)"); - ops_rename(LR"(mfolder4\temp_mfilemoveover.txt)", LR"(mfolder4\mfilemoveover.txt)", - true); - ops_read(LR"(mfolder4\mfilemoveover.txt)"); - verify_source_existance(LR"(overwrite\mfolder4\temp_mfilemoveover.txt)", false); - if (bool protect_virtualized = false) { - verify_source_contents(LR"(mod4\mfolder4\mfilemoveover.txt)", - old_contents.c_str()); - verify_source_contents(LR"(overwrite\mfolder4\mfilemoveover.txt)", - R"(mfolder4\mfilemoveover.txt overwrite)"); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfilemoveover.txt)", - R"(mfolder4\mfilemoveover.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfilemoveover.txt)", false); - } - } - - { - const auto& old_contents = - source_contents(LR"(mod4\mfolder4\mfiledeletewrite.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite.txt)", - old_contents.c_str()); - ops_deleteoverwrite(LR"(mfolder4\mfiledeletewrite.txt)", - R"(mfolder4\mfiledeletewrite.txt overwrite)", false); - ops_read(LR"(mfolder4\mfiledeletewrite.txt)"); - if (bool protect_virtualized = false) { - verify_source_contents(LR"(overwrite\mfolder4\mfiledeletewrite.txt)", - R"(mfolder4\mfiledeletewrite.txt overwrite)"); - if (auto proper_delete_implemented = false) - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite.txt)", - old_contents.c_str()); - else - verify_source_existance(LR"(mod4\mfolder4\mfiledeletewrite.txt)", false); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite.txt)", - R"(mfolder4\mfiledeletewrite.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfiledeletewrite.txt)", false); - } - } - - { - const auto& old_contents = - source_contents(LR"(mod4\mfolder4\mfiledeletewrite2p.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite2p.txt)", - old_contents.c_str()); - ops_delete(LR"(mfolder4\mfiledeletewrite2p.txt)"); - ops_overwrite(LR"(mfolder4\mfiledeletewrite2p.txt)", - R"(mfolder4\mfiledeletewrite2p.txt overwrite)", false); - ops_read(LR"(mfolder4\mfiledeletewrite2p.txt)"); - if (bool protect_virtualized_or_track_deleted_only_in_current_process = true) { - verify_source_contents(LR"(overwrite\mfolder4\mfiledeletewrite2p.txt)", - R"(mfolder4\mfiledeletewrite2p.txt overwrite)"); - if (auto proper_delete_implemented = false) - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite2p.txt)", - old_contents.c_str()); - else - verify_source_existance(LR"(mod4\mfolder4\mfiledeletewrite2p.txt)", false); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfiledeletewrite2p.txt)", - R"(mfolder4\mfiledeletewrite2p.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfiledeletewrite2p.txt)", false); - } - } - - { - const auto& old_contents = source_contents(LR"(mod4\mfolder4\mfiledeletemove.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove.txt)", - old_contents.c_str()); - ops_overwrite(LR"(mfolder4\temp_mfiledeletemove.txt)", - R"(mfolder4\mfiledeletemove.txt overwrite)", false); - verify_source_contents(LR"(overwrite\mfolder4\temp_mfiledeletemove.txt)", - R"(mfolder4\mfiledeletemove.txt overwrite)"); - ops_deleterename(LR"(mfolder4\temp_mfiledeletemove.txt)", - LR"(mfolder4\mfiledeletemove.txt)"); - ops_read(LR"(mfolder4\mfiledeletemove.txt)"); - verify_source_existance(LR"(overwrite\mfolder4\temp_mfiledeletemove.txt)", false); - if (bool protect_virtualized = false) { - verify_source_contents(LR"(overwrite\mfolder4\mfiledeletemove.txt)", - R"(mfolder4\mfiledeletemove.txt overwrite)"); - if (auto proper_delete_implemented = false) - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove.txt)", - old_contents.c_str()); - else - verify_source_existance(LR"(mod4\mfolder4\mfiledeletemove.txt)", false); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove.txt)", - R"(mfolder4\mfiledeletemove.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfiledeletemove.txt)", false); - } - } - - { - const auto& old_contents = - source_contents(LR"(mod4\mfolder4\mfiledeletemove2p.txt)"); - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove2p.txt)", - old_contents.c_str()); - ops_delete(LR"(mfolder4\mfiledeletemove2p.txt)"); - ops_overwrite(LR"(mfolder4\temp_mfiledeletemove2p.txt)", - R"(mfolder4\mfiledeletemove2p.txt overwrite)", false); - verify_source_contents(LR"(overwrite\mfolder4\temp_mfiledeletemove2p.txt)", - R"(mfolder4\mfiledeletemove2p.txt overwrite)"); - ops_rename(LR"(mfolder4\temp_mfiledeletemove2p.txt)", - LR"(mfolder4\mfiledeletemove2p.txt)", false); - ops_read(LR"(mfolder4\mfiledeletemove2p.txt)"); - verify_source_existance(LR"(overwrite\mfolder4\temp_mfiledeletemove2p.txt)", false); - if (bool protect_virtualized_or_track_deleted_only_in_current_process = true) { - verify_source_contents(LR"(overwrite\mfolder4\mfiledeletemove2p.txt)", - R"(mfolder4\mfiledeletemove2p.txt overwrite)"); - if (auto proper_delete_implemented = false) - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove2p.txt)", - old_contents.c_str()); - else - verify_source_existance(LR"(mod4\mfolder4\mfiledeletemove2p.txt)", false); - } else { - verify_source_contents(LR"(mod4\mfolder4\mfiledeletemove2p.txt)", - R"(mfolder4\mfiledeletemove2p.txt overwrite)"); - verify_source_existance(LR"(overwrite\mfolder4\mfiledeletemove2p.txt)", false); - } - } - - // test copy on write/delete/move against original mount files: - - { - const auto& old_contents = mount_contents(LR"(rfolder\rfilerewrite.txt)"); - ops_rewrite(LR"(rfolder\rfilerewrite.txt)", R"(rfolder\rfilerewrite.txt rewrite)"); - ops_read(LR"(rfolder\rfilerewrite.txt)"); - if (auto copy_on_readwrite_implemented = false) { - verify_mount_contents(LR"(rfolder\rfilerewrite.txt)", old_contents.c_str()); - verify_source_contents(LR"(overwrite\rfolder\rfilerewrite.txt)", - R"(rfolder\rfilerewrite.txt rewrite)"); - } else { - verify_mount_contents(LR"(rfolder\rfilerewrite.txt)", - R"(rfolder\rfilerewrite.txt rewrite)"); - verify_source_existance(LR"(overwrite\rfolder\rfilerewrite.txt)", false); - } - } - ops_overwrite(LR"(rfolder\rfilerewrite.txt\fail)", - R"(rfilerewrite.txt is a file so folder creation should fail)", true, - false); - verify_mount_existance( - LR"(rfolder\rfilerewrite.txt)"); // verifies its a file and not a directory - if (auto copy_on_readwrite_implemented = false) - verify_source_existance( - LR"(overwrite\rfolder\rfilerewrite.txt)"); // verifies its a file and not a - // directory - else - verify_source_existance(LR"(overwrite\rfolder\rfilerewrite.txt)", false); - ops_overwrite(LR"(rfolder\rfile0.txt\fail)", - R"(rfile0.txt is a file so folder creation should fail)", true, false); - verify_mount_existance( - LR"(rfolder\rfile0.txt)"); // verifies its a file and not a directory - - { - const auto& old_contents = mount_contents(LR"(rfolder\rfiledeletewrite.txt)"); - ops_deleteoverwrite(LR"(rfolder\rfiledeletewrite.txt)", - R"(rfolder\rfiledeletewrite.txt overwrite)", false); - ops_read(LR"(rfolder\rfiledeletewrite.txt)"); - if (bool protect_virtualized = false) { - verify_source_contents(LR"(overwrite\rfolder\rfiledeletewrite.txt)", - R"(rfolder\rfiledeletewrite.txt overwrite)"); - if (auto proper_delete_implemented = false) - verify_mount_contents(LR"(rfolder\rfiledeletewrite.txt)", old_contents.c_str()); - else - verify_mount_existance(LR"(rfolder\rfiledeletewrite.txt)", false); - } else { - verify_mount_contents(LR"(rfolder\rfiledeletewrite.txt)", - R"(rfolder\rfiledeletewrite.txt overwrite)"); - verify_source_existance(LR"(overwrite\rfolder\rfiledeletewrite.txt)", false); - } - } - - { - const auto& old_contents = mount_contents(LR"(rfolder\rfiledelete.txt)"); - ops_delete(LR"(rfolder\rfiledelete.txt)"); - ops_read(LR"(rfolder\rfiledelete.txt)", false); - if (auto proper_delete_implemented = false) - verify_mount_contents(LR"(rfolder\rfiledelete.txt)", old_contents.c_str()); - else - verify_mount_existance(LR"(rfolder\rfiledelete.txt)", false); - } - - { - const auto& old_contents = mount_contents(LR"(rfolder\rfileoldname.txt)"); - ops_rename(LR"(rfolder\rfileoldname.txt)", LR"(rfolder\rfilenewname.txt)", false, - false); - ops_read(LR"(rfolder\rfileoldname.txt)", false); - ops_read(LR"(rfolder\rfilenewname.txt)"); - verify_source_contents(LR"(overwrite\rfolder\rfilenewname.txt)", - old_contents.c_str()); - verify_mount_existance(LR"(rfolder\rfilenewname.txt)", false); - if (auto copy_on_move_implemented = false) - verify_mount_contents(LR"(rfolder\rfileoldname.txt)", old_contents.c_str()); - else - verify_mount_existance(LR"(rfolder\rfileoldname.txt)", false); - } - - ops_list(LR"(.)", true, true); - - return true; -} diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.h b/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.h deleted file mode 100644 index bec81f2..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_basic_test.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "usvfs_test_base.h" - -class usvfs_basic_test : public usvfs_test_base -{ -public: - static constexpr auto SCENARIO_NAME = "basic"; - - usvfs_basic_test(const usvfs_test_options& options) : usvfs_test_base(options) {} - - virtual const char* scenario_name(); - virtual bool scenario_run(); -}; diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test.cpp b/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test.cpp deleted file mode 100644 index e6e6bb1..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test.cpp +++ /dev/null @@ -1,242 +0,0 @@ - -#include <iostream> -#include <memory> - -#include <boost/filesystem.hpp> -#include <boost/type_traits.hpp> -#include <winapi.h> - -#include "usvfs_basic_test.h" -#include <stringcast.h> - -void print_usage(const std::wstring& exe_name, const std::wstring& test_name) -{ - using namespace std; - wcerr << "usage: " << exe_name << " [<options>] <scenario>[:<label>]" << endl; - wcerr << "available options:" << endl; - wcerr << " -ops32 : force 32bit file opertations process (default is same " - "bitness)." - << endl; - wcerr << " -ops64 : force 64bit file opertations process (default is same " - "bitness)." - << endl; - wcerr << " -opsexe <file> : full path to file operations executable (overrides " - "-ops32/64)." - << endl; - wcerr << " -opsarg <arg> : adds argument to the start of all file operations." - << endl; - wcerr << " -fixture <dir> : fixture dir (default is test\\fixtures\\" << test_name - << "\\<scenario>)." << endl; - wcerr << " -mapping <file> : mapping file (default is <fixture " - "dir>\\vfs_mappings.txt)." - << endl; - wcerr << " -temp <dir> : temp dir (default is test\\temp\\" << test_name - << "\\<scenario>_<label>)." << endl; - wcerr << " -mount <dir> : mount dir (default is <temp dir>\\mount)." << endl; - wcerr << " -source <dir> : source dir (default is <temp dir>\\source)." << endl; - wcerr << " -out <file> : output file (default is <temp " - "dir>\\<scenario>_<label>.log)." - << endl; - wcerr << " -usvfslog <file> : output file (default is <temp " - "dir>\\<scenario>_<label>_usvfs.log)." - << endl; - wcerr << " -forcetemprecursivedelete : decimate temp dir even if doesn't look like a " - "temp dir." - << endl; - wcerr << endl; - wcerr << "note: if <label> is ommited the current platform (x86/x64) is used." - << endl; - wcerr << "note: mount and source dirs should not exist or be empty directories. if " - "either of them" - << endl; - wcerr << "is not given, the temp dir is first recusrively deleted. to protect " - "against accidents a" - << endl; - wcerr << "heuristic is used to verify the temp dir only has entires which this test " - "creates." - << endl; - wcerr << "-forcetemprecursivedelete can be used to circumvent this heuristic." - << endl; -} - -usvfs_test_base* find_scenario(const std::string& scenario, - const usvfs_test_options& options) -{ - if (scenario == usvfs_basic_test::SCENARIO_NAME) - return new usvfs_basic_test(options); - else - return nullptr; -} - -bool verify_args_exist(const wchar_t* flag, int params, int index, int count) -{ - if (index + params >= count) { - std::wcerr << flag << L" requires " << params << L" arguments" << std::endl; - return false; - } else - return true; -} - -bool verify_file(const test::path& file) -{ - bool is_dir = false; - if (!winapi::ex::wide::fileExists(file.c_str(), &is_dir) || is_dir) { - std::wcerr << L"File does not exist: " << file << std::endl; - return false; - } else - return true; -} - -bool verify_dir(const test::path& dir) -{ - bool is_dir = false; - if (!winapi::ex::wide::fileExists(dir.c_str(), &is_dir) || !is_dir) { - std::wcerr << L"Directory does not exist: " << dir << std::endl; - return false; - } else - return true; -} - -std::wstring get_env_var(const wchar_t* var) -{ - using namespace std; - size_t size = GetEnvironmentVariableW(var, nullptr, 0); - if (size) { - std::wstring value(size, L'\0'); - size = GetEnvironmentVariableW(var, &value[0], static_cast<DWORD>(value.size())); - if (size && size < value.size()) { - value.resize(size); - return value; - } - } - wcerr << L"GetEnvironmentVariableW(" << var << L") failed result=" << size - << L" error=" << GetLastError() << endl; - return wstring(); -} - -bool set_env_var(const wchar_t* var, const std::wstring& value) -{ - if (SetEnvironmentVariableW(var, value.c_str())) - return true; - std::wcerr << L"SetEnvironmentVariableW(" << var << L"," << value - << L") failed error=" << GetLastError() << std::endl; - return false; -} - -int wmain(int argc, wchar_t* argv[]) -{ - using namespace std; - using namespace test; - - const wchar_t* label = nullptr; - wstring scenario; - path temp; - usvfs_test_options options; - - for (int ai = 1; ai < argc; ++ai) { - if (wcscmp(argv[ai], L"-ops32") == 0) - options.set_ops32(); - else if (wcscmp(argv[ai], L"-ops64") == 0) - options.set_ops64(); - else if (wcscmp(argv[ai], L"-opsexe") == 0) { - if (!verify_args_exist(L"-opsexe", 1, ai, argc)) - return 1; - options.opsexe = argv[++ai]; - if (!verify_file(options.opsexe)) - return 1; - } else if (wcscmp(argv[ai], L"-opsarg") == 0) { - if (!verify_args_exist(L"-opsarg", 1, ai, argc)) - return 1; - options.add_ops_options(argv[++ai]); - } else if (wcscmp(argv[ai], L"-fixture") == 0) { - if (!verify_args_exist(L"-fixture", 1, ai, argc)) - return 1; - options.fixture = argv[++ai]; - if (!verify_dir(options.fixture)) - return 1; - } else if (wcscmp(argv[ai], L"-mapping") == 0) { - if (!verify_args_exist(L"-mapping", 1, ai, argc)) - return 1; - options.mapping = argv[++ai]; - if (!verify_file(options.mapping)) - return 1; - } else if (wcscmp(argv[ai], L"-temp") == 0) { - if (!verify_args_exist(L"-temp", 1, ai, argc)) - return 1; - options.mount = argv[++ai]; - } else if (wcscmp(argv[ai], L"-mount") == 0) { - if (!verify_args_exist(L"-mount", 1, ai, argc)) - return 1; - options.mount = argv[++ai]; - } else if (wcscmp(argv[ai], L"-source") == 0) { - if (!verify_args_exist(L"-source", 1, ai, argc)) - return 1; - options.source = argv[++ai]; - } else if (wcscmp(argv[ai], L"-out") == 0) { - if (!verify_args_exist(L"-out", 1, ai, argc)) - return 1; - options.output = argv[++ai]; - } else if (wcscmp(argv[ai], L"-usvfslog") == 0) { - if (!verify_args_exist(L"-usvfslog", 1, ai, argc)) - return 1; - options.usvfs_log = argv[++ai]; - } else if (wcscmp(argv[ai], L"-forcetemprecursivedelete") == 0) - options.force_temp_cleanup = true; - else if (argv[ai][0] == '-') { - wcerr << L"Unknown option " << argv[ai] << endl; - return 1; - } else if (!scenario.empty()) { - wcerr << L"Multiple scenarios can not be specified: " << scenario.c_str() << L", " - << argv[ai] << endl; - return 1; - } else { - label = wcschr(argv[ai], ':'); - if (label) { - scenario = std::wstring(static_cast<const wchar_t*>(argv[ai]), label); - ++label; // skip the ':' - } else - scenario = argv[ai]; - - if (scenario.empty()) { - wcerr << L"Scenario name can not be empty!" << endl; - return 1; - } - } - } - - path test_name{"usvfs_test"}; - - if (scenario.empty()) { - print_usage(path(argv[0]).stem(), test_name); - return 1; - } - - options.fill_defaults(test_name, scenario, label); - - unique_ptr<usvfs_test_base> test{find_scenario( - usvfs::shared::string_cast<std::string>(scenario, usvfs::shared::CodePage::UTF8), - options)}; - if (!test) { - wcerr << L"ERROR: Unknown scenario specified: " << scenario.c_str() << endl; - return 2; - } - - auto dllPath = path_of_usvfs_lib(platform_dependant_executable("usvfs", "dll")); - ScopedLoadLibrary loadDll(dllPath.c_str()); - if (!loadDll) { - wcerr << L"ERROR: failed to load usvfs dll: " << dllPath.c_str() << L", " - << GetLastError() << endl; - return 3; - } - - // In order for bin\usvfs_proxy_x??.exe to find the lib\usvfs_x??.dll file we add the - // lib folder to our path: - std::wstring env_path = get_env_var(L"Path"); - if (env_path.empty() || - !set_env_var(L"Path", dllPath.parent_path().wstring() + L";" + env_path)) { - wcerr << L"ERROR: failed to add usvfs dll directory to path" << endl; - return 3; - } - - return test->run(path(argv[0]).stem()); -} diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp b/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp deleted file mode 100644 index bfa391e..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.cpp +++ /dev/null @@ -1,1002 +0,0 @@ - -#include "usvfs_test_base.h" -#include <boost/filesystem.hpp> -#include <boost/type_traits.hpp> -#include <cctype> -#include <cerrno> -#include <chrono> -#include <future> -#include <iostream> -#include <stringcast.h> -#include <thread> -#include <unordered_set> -#include <usvfs.h> -#include <vector> -#include <winapi.h> - -using test::throw_testWinFuncFailed; - -// usvfs_test_options class: - -void usvfs_test_options::fill_defaults(const path& test_name, - const std::wstring& scenario, - const wchar_t* label) -{ - using namespace test; - - std::wstring lbl; - if (label) - lbl = label; - -#ifdef _WIN64 - set_ops64(); - if (!label) - lbl = L"x64"; -#else - set_ops32(); - if (!label) - lbl = L"x86"; -#endif - - std::wstring scenario_label = scenario; - if (!lbl.empty()) { - scenario_label += L"_"; - scenario_label += lbl; - } - - if (fixture.empty()) - fixture = path_of_test_fixtures(test_name / scenario); - - if (mapping.empty()) - mapping = fixture / DEFAULT_MAPPING; - - if (temp.empty()) { - temp = path_of_test_temp(test_name / scenario_label); - } - - if (mount.empty()) { - mount = temp / MOUNT_DIR; - temp_cleanup = true; - } - - if (source.empty()) { - source = temp / SOURCE_DIR; - temp_cleanup = true; - } - - if (output.empty()) { - output = temp / scenario_label; - output += ".log"; - } - - if (usvfs_log.empty()) { - usvfs_log = temp / scenario_label; - usvfs_log += "_usvfs.log"; - } -} - -void usvfs_test_options::set_ops32() -{ - if (opsexe.empty()) - opsexe = test::path_of_test_bin(L"test_file_operations_x86.exe"); -} - -void usvfs_test_options::set_ops64() -{ - if (opsexe.empty()) - opsexe = test::path_of_test_bin(L"test_file_operations_x64.exe"); -} - -void usvfs_test_options::add_ops_options(const std::wstring& options) -{ - if (!ops_options.empty()) - ops_options += L" "; - ops_options += options; -} - -// usvfs_connector helper class: - -class usvfs_connector -{ -public: - using path = test::path; - - usvfs_connector(const usvfs_test_options& options) - : m_usvfs_log(test::ScopedFILE::open(options.usvfs_log, L"wt")), - m_exit_future(m_exit_signal.get_future()) - { - winapi::ex::wide::createPath(options.usvfs_log.parent_path().c_str()); - - std::wcout << "Connecting VFS..." << std::endl; - - std::unique_ptr<usvfsParameters, decltype(&usvfsFreeParameters)> parameters{ - usvfsCreateParameters(), &usvfsFreeParameters}; - - usvfsSetInstanceName(parameters.get(), "usvfs_test"); - usvfsSetDebugMode(parameters.get(), false); - usvfsSetLogLevel(parameters.get(), LogLevel::Debug); - usvfsSetCrashDumpType(parameters.get(), CrashDumpsType::None); - usvfsSetCrashDumpPath(parameters.get(), ""); - - usvfsInitLogging(false); - usvfsCreateVFS(parameters.get()); - - m_log_thread = std::thread(&usvfs_connector::usvfs_logger, this); - } - - ~usvfs_connector() - { - usvfsDisconnectVFS(); - m_exit_signal.set_value(); - m_log_thread.join(); - } - - enum class map_type - { - none, // the mapping_reader uses this value but will never pass it to the - // usvfs_connector (which ignored such entries) - dir, - dircreate, - file - }; - - struct mapping - { - mapping(map_type itype, std::wstring isource, std::wstring idestination) - : type(itype), source(isource), destination(idestination) - {} - - map_type type; - path source; - path destination; - }; - - using mappings_list = std::vector<mapping>; - - void updateMapping(const mappings_list& mappings, const usvfs_test_options& options, - FILE* log) - { - using namespace std; - using namespace usvfs::shared; - - fprintf(log, "Updating VFS mappings:\n"); - - usvfsClearVirtualMappings(); - - for (const auto& map : mappings) { - const string& source = - usvfs_test_base::SOURCE_LABEL + - test::path_as_relative(options.source, map.source).string(); - const string& destination = - usvfs_test_base::MOUNT_LABEL + - test::path_as_relative(options.mount, map.destination).string(); - switch (map.type) { - case map_type::dir: - fprintf(log, " mapdir: %s => %s\n", source.c_str(), destination.c_str()); - usvfsVirtualLinkDirectoryStatic(map.source.c_str(), map.destination.c_str(), - LINKFLAG_RECURSIVE); - break; - - case map_type::dircreate: - fprintf(log, " mapdircreate: %s => %s\n", source.c_str(), destination.c_str()); - usvfsVirtualLinkDirectoryStatic(map.source.c_str(), map.destination.c_str(), - LINKFLAG_CREATETARGET | LINKFLAG_RECURSIVE); - break; - - case map_type::file: - fprintf(log, " mapfile: %s => %s\n", source.c_str(), destination.c_str()); - usvfsVirtualLinkFile(map.source.c_str(), map.destination.c_str(), - LINKFLAG_RECURSIVE); - break; - } - } - - fprintf(log, "\n"); - } - - static DWORD spawn(wchar_t* commandline) - { - using namespace usvfs::shared; - - STARTUPINFO si{0}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{0}; - - if (!usvfsCreateProcessHooked(NULL, commandline, NULL, NULL, FALSE, 0, NULL, NULL, - &si, &pi)) - throw_testWinFuncFailed( - "CreateProcessHooked", - string_cast<std::string>(commandline, CodePage::UTF8).c_str()); - - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exit = 99; - if (!GetExitCodeProcess(pi.hProcess, &exit)) { - test::WinFuncFailedGenerator failed; - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - throw failed("GetExitCodeProcess"); - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - return exit; - } - - void usvfs_logger() - { - fprintf(m_usvfs_log, "usvfs_test usvfs logger started:\n"); - fflush(m_usvfs_log); - - constexpr size_t size = 1024; - char buf[size + 1]{0}; - int noLogCycles = 0; - std::chrono::milliseconds wait_for; - do { - if (usvfsGetLogMessages(buf, size, false)) { - fwrite(buf, 1, strlen(buf), m_usvfs_log); - fwrite("\n", 1, 1, m_usvfs_log); - fflush(m_usvfs_log); - noLogCycles = 0; - } else - ++noLogCycles; - if (noLogCycles) - wait_for = std::chrono::milliseconds(std::min(40, noLogCycles) * 5); - else - wait_for = std::chrono::milliseconds(0); - } while (m_exit_future.wait_for(wait_for) == std::future_status::timeout); - - while (usvfsGetLogMessages(buf, size, false)) { - fwrite(buf, 1, strlen(buf), m_usvfs_log); - fwrite("\n", 1, 1, m_usvfs_log); - } - - fprintf(m_usvfs_log, "usvfs log closed.\n"); - m_usvfs_log.close(); - } - -private: - test::ScopedFILE m_usvfs_log; - std::thread m_log_thread; - std::promise<void> m_exit_signal; - std::shared_future<void> m_exit_future; -}; - -// mappings_reader - -class mappings_reader -{ -public: - using path = test::path; - using string = std::string; - using wstring = std::wstring; - using map_type = usvfs_connector::map_type; - using mapping = usvfs_connector::mapping; - using mappings_list = usvfs_connector::mappings_list; - - mappings_reader(const path& mount_base, const path& source_base) - : m_mount_base(mount_base), m_source_base(source_base) - {} - - mappings_list read(const path& mapfile) - { - const auto map = test::ScopedFILE::open(mapfile, L"rt"); - mappings_list mappings; - - char line[1024]; - while (!feof(map)) { - // read one line: - if (!fgets(line, _countof(line), map)) - if (feof(map)) - break; - else - throw_testWinFuncFailed("fgets", "reading mappings"); - - if (empty_line(line)) - continue; - - if (start_nesting(line, "mapdir")) - m_nesting = map_type::dir; - else if (start_nesting(line, "mapdircreate")) - m_nesting = map_type::dircreate; - else if (start_nesting(line, "mapfile")) - m_nesting = map_type::file; - else if (!isspace(*line)) // mapping sources should be indented and we already - // check all the mapping directives - throw test::FuncFailed("mappings_reader::read", "invalid mappings file line", - line); - else { - const auto& source_rel = trimmed_wide_string(line); - mappings.push_back(mapping(m_nesting, m_source_base / source_rel, m_mount)); - } - } - - return mappings; - } - - bool start_nesting(const char* line, const char* directive) - { - // check if line starts with directive and if so skip it: - auto dlen = strlen(directive); - auto after = line + dlen; - if (strncmp(directive, line, dlen) == 0 && (!*after || isspace(*after))) { - m_mount = m_mount_base; - const auto& mount_rel = trimmed_wide_string(after); - if (!mount_rel.empty()) - m_mount /= mount_rel; - return true; - } else - return false; - } - - static wstring trimmed_wide_string(const char* in) - { - while (std::isspace(*in)) - ++in; - auto end = in; - end += strlen(end); - while (end > in && std::isspace(*(end - 1))) - --end; - return usvfs::shared::string_cast<wstring>(string(in, end), - usvfs::shared::CodePage::UTF8); - } - - static bool empty_line(const char* line) - { - for (; *line; ++line) { - if (*line == '#') // comment, ignore rest of line - return true; - else if (!std::isspace(*line)) - return false; - } - return true; - } - -private: - path m_mount_base; - path m_source_base; - path m_mount; - map_type m_nesting = map_type::none; -}; - -// usvfs_test_base class: - -void usvfs_test_base::cleanup_temp() -{ - using namespace test; - using namespace winapi::ex::wide; - - bool isDir = false; - if (!m_o.temp_cleanup || !fileExists(m_o.temp.c_str(), &isDir)) - return; - - if (!isDir) { - if (m_o.force_temp_cleanup) - delete_file(m_o.temp); - else - throw FuncFailed("cleanup_temp", "temp exists but is a file", - m_o.temp.string().c_str()); - } else { - std::vector<wstring> cleanfiles; - std::vector<wstring> cleandirs; - std::vector<wstring> otherdirs; - bool output_file = false; - for (auto f : quickFindFiles(m_o.temp.c_str(), L"*")) - if (f.fileName == L"." || f.fileName == L"..") - continue; - else if ((f.attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - if (f.fileName == m_o.output.filename()) - output_file = true; - cleanfiles.push_back(f.fileName); - } else if (f.fileName == SOURCE_DIR || f.fileName == MOUNT_DIR) - cleandirs.push_back(f.fileName); - else - otherdirs.push_back(f.fileName); - if (!cleanfiles.empty() || !cleandirs.empty() || !otherdirs.empty()) { - if (!m_o.force_temp_cleanup && !otherdirs.empty()) - throw FuncFailed("cleanup_temp", - "Refusing to clean temp dir with non-mount/source directories " - "(clean manually and rerun)", - m_o.temp.string().c_str()); - if (!m_o.force_temp_cleanup && cleandirs.empty() && !output_file) - throw FuncFailed("cleanup_temp", - "Refusing to clean temp dir with no directories and no output " - "log (clean manually and rerun)", - m_o.temp.string().c_str()); - std::wcout << "Cleaning previous temp dir: " << m_o.temp.c_str() << std::endl; - for (auto f : cleanfiles) - delete_file(m_o.temp / f); - for (auto d : cleandirs) - recursive_delete_files(m_o.temp / d); - for (auto d : otherdirs) - recursive_delete_files(m_o.temp / d); - } - } -} - -void usvfs_test_base::copy_fixture() -{ - using namespace test; - using namespace winapi::ex::wide; - - path fmount = m_o.fixture / MOUNT_DIR; - path fsource = m_o.fixture / SOURCE_DIR; - - bool isDir = false; - if (!fileExists(fmount.c_str(), &isDir) || !isDir) - throw FuncFailed("copy_fixture", "fixtures dir does not exist", - fmount.string().c_str()); - if (!fileExists(fsource.c_str(), &isDir) || !isDir) - throw FuncFailed("copy_fixture", "fixtures dir does not exist", - fsource.string().c_str()); - if (fileExists(m_o.mount.c_str(), &isDir)) - throw FuncFailed("copy_fixture", "source dir already exists", - m_o.mount.string().c_str()); - if (fileExists(m_o.source.c_str(), &isDir)) - throw FuncFailed("copy_fixture", "source dir already exists", - m_o.source.string().c_str()); - - std::wcout << "Copying fixture: " << m_o.fixture << std::endl; - recursive_copy_files(fmount, m_o.mount, false); - recursive_copy_files(fsource, m_o.source, false); -} - -bool usvfs_test_base::postmortem_check() -{ - path gold_output = m_o.fixture / m_o.output.filename(); - - { - const auto& log = output(); - - path mount_gold = MOUNT_DIR; - mount_gold += POSTMORTEM_SUFFIX; - path source_gold = SOURCE_DIR; - source_gold += POSTMORTEM_SUFFIX; - - bool is_dir = false; - if (!winapi::ex::wide::fileExists(m_o.mount.c_str(), &is_dir) || !is_dir) { - fprintf(log, " ERROR: mount directory does not exist?!\n"); - return false; - } - if (!winapi::ex::wide::fileExists(m_o.source.c_str(), &is_dir) || !is_dir) { - fprintf(log, " ERROR: source directory does not exist?!\n"); - return false; - } - if (!winapi::ex::wide::fileExists((m_o.fixture / mount_gold).c_str(), &is_dir) || - !is_dir) { - fprintf(log, " ERROR: fixtures golden mount does not exist: %s\n", - mount_gold.string().c_str()); - return false; - } - if (!winapi::ex::wide::fileExists((m_o.fixture / source_gold).c_str(), &is_dir) || - !is_dir) { - fprintf(log, " ERROR: fixtures golden source does not exist: %s\n", - mount_gold.string().c_str()); - return false; - } - if (!winapi::ex::wide::fileExists(gold_output.c_str(), &is_dir) || is_dir) { - fprintf(log, " ERROR: golden scenario output does not exist: %s\n", - gold_output.filename().string().c_str()); - return false; - } - - fprintf(log, "postmortem check of %s against golden %s...\n", - m_o.mount.filename().string().c_str(), mount_gold.string().c_str()); - bool mount_check = - recursive_compare_dirs(path(), m_o.fixture / mount_gold, m_o.mount, log); - - fprintf(log, "postmortem check of %s against golden %s...\n", - m_o.source.filename().string().c_str(), source_gold.string().c_str()); - bool source_check = - recursive_compare_dirs(path(), m_o.fixture / source_gold, m_o.source, log); - - if (mount_check && source_check) - fprintf(log, "postmortem check successful.\n"); - else { - fprintf(log, "ERROR: postmortem check failed!\n"); - return false; - } - } // close output before comparing it - - // don't print anything more to the output (except maybe errors), - // so that the final output can be copied as is to the fixtures (when updating the - // golden version) - - // block remove 2024/06/07 - one would need to regenerate a gold output for this but - // I am not sure this is still relevant - // - // if (!test::compare_files(gold_output, m_o.output, false)) { - // fprintf(output(), "ERROR: output does not match gold output: %s\n", - // m_o.output.filename().string().c_str()); return false; - //} - - return true; -} - -bool usvfs_test_base::recursive_compare_dirs(path rel_path, path gold_base, - path result_base, FILE* log) -{ - path result_full = result_base / rel_path; - path gold_full = gold_base / rel_path; - - std::unordered_set<std::wstring> gold_dirs; - std::unordered_set<std::wstring> gold_files; - for (const auto& f : winapi::ex::wide::quickFindFiles(gold_full.c_str(), L"*")) { - if (f.fileName == L"." || f.fileName == L"..") - continue; - if (f.attributes & FILE_ATTRIBUTE_DIRECTORY) - gold_dirs.insert(f.fileName); - else - gold_files.insert(f.fileName); - } - - bool all_good = true; - - std::vector<std::wstring> recurse; - for (const auto& f : winapi::ex::wide::quickFindFiles(result_full.c_str(), L"*")) { - if (f.fileName == L"." || f.fileName == L"..") - continue; - if (f.attributes & FILE_ATTRIBUTE_DIRECTORY) { - const auto& find = gold_dirs.find(f.fileName); - if (find != gold_dirs.end()) { - gold_dirs.erase(find); - recurse.push_back(f.fileName); - } else { - fprintf(log, " unexpected directory found: %s%s\n", MOUNT_LABEL, - (rel_path / f.fileName).string().c_str()); - all_good = false; - } - } else { - const auto& find = gold_files.find(f.fileName); - if (find != gold_files.end()) { - gold_files.erase(find); - if (!test::compare_files(gold_full / f.fileName, result_full / f.fileName, - false)) { - fprintf(log, " file contents differs: %s%s\n", MOUNT_LABEL, - (rel_path / f.fileName).string().c_str()); - all_good = false; - } - } else { - fprintf(log, " unexpected file found: %s%s\n", MOUNT_LABEL, - (rel_path / f.fileName).string().c_str()); - all_good = false; - } - } - } - - for (auto d : gold_dirs) { - fprintf(log, " expected directory not found: %s%s\n", MOUNT_LABEL, - (rel_path / d).string().c_str()); - all_good = false; - } - - for (auto f : gold_files) { - fprintf(log, " expected file not found: %s%s\n", MOUNT_LABEL, - (rel_path / f).string().c_str()); - all_good = false; - } - - for (auto r : recurse) - all_good &= recursive_compare_dirs(rel_path / r, gold_base, result_base, log); - - return all_good; -} - -test::ScopedFILE usvfs_test_base::output() -{ - auto log = test::ScopedFILE::open(m_o.output, m_clean_output ? L"wt" : L"at"); - m_clean_output = false; - return log; -} - -void usvfs_test_base::clean_output() -{ - using namespace std; - - errno_t err; - auto in = test::ScopedFILE::open(m_o.output, L"rt", err); - if (err == ENOENT) { - wcerr << L"warning: no " << m_o.output << L" to clean." << endl; - return; - } else if (err || !in) - throw_testWinFuncFailed("_wfopen_s", m_o.output.string().c_str(), err); - - path clean = m_o.output.parent_path() / m_o.output.stem(); - clean += OUTPUT_CLEAN_SUFFIX; - clean += m_o.output.extension(); - - auto out = test::ScopedFILE::open(clean, L"wt"); - wcout << L"Cleaning " << m_o.output << " to " << clean << endl; - - char line[1024]; - while (!feof(in)) { - // read one line: - if (!fgets(line, _countof(line), in)) - if (feof(in)) - break; - else - throw_testWinFuncFailed("fgets", "reading output"); - if (*line == '#') - continue; - - // in order for the clean output to compare cleanly between run with different - // options we clean out things like the platform and the ops log name (which - // contians the scenario label): - - char* platform = line; - while (platform) { - char* platform_x86 = strstr(platform, "x86"); - char* platform_x64 = strstr(platform, "x64"); - if (platform_x86 && platform_x64) - platform = std::min(platform_x86, platform_x64); - else if (platform_x86) - platform = platform_x86; - else if (platform_x64) - platform = platform_x64; - else - platform = nullptr; - if (platform) { - platform[1] = platform[2] = '?'; - platform += 3; - } - } - - char* cout_end = strstr(line, "-cout+ "); - char* cout_log_end = nullptr; - if (cout_end) { - cout_end += strlen("-cout+ "); - cout_log_end = strchr(cout_end, ' '); - } - if (cout_log_end && cout_log_end > cout_end) { - cout_end[0] = '?'; - if (cout_log_end > cout_end + 1) - memmove(cout_end + 1, cout_log_end, strlen(cout_log_end) + 1); - } - - fputs(line, out); - } -} - -int usvfs_test_base::run(const std::wstring& exe_name) -{ - using namespace usvfs::shared; - using namespace std; - - int res = run_impl(exe_name); - try { - clean_output(); - } catch (const exception& e) { - wcerr << "CERROR: " << string_cast<wstring>(e.what(), CodePage::UTF8).c_str() - << endl; - } catch (...) { - wcerr << "CERROR: unknown exception" << endl; - } - if (!res) - wcout << "scenario " << scenario_name() << " PASSED." << endl; - else - wcerr << "scenario " << scenario_name() << " FAILED!" << endl; - return res; -} - -int usvfs_test_base::run_impl(const std::wstring& exe_name) -{ - using namespace usvfs::shared; - using namespace std; - - try { - winapi::ex::wide::createPath(m_o.output.parent_path().c_str()); - - // we read mappings first only because it is "non-destructive" but might raise an - // error if mappings invalid - auto mappings = mappings_reader(m_o.mount, m_o.source).read(m_o.mapping); - - cleanup_temp(); - log_settings(exe_name); - copy_fixture(); - - usvfs_connector usvfs(m_o); - { - const auto& log = output(); - usvfs.updateMapping(mappings, m_o, log); - - fprintf(log, "running scenario %s:\n\n", scenario_name()); - } - auto res = scenario_run(); - { - const auto& log = output(); - if (res) - fprintf(log, "\nscenario ended successfully!\n\n"); - else - fprintf(log, "\nscenario failed miserably.\n"); - } - - if (!res) - return 7; - - if (!postmortem_check()) - return 8; - - return 0; - } -#if 1 // just a convient way to not catch exception when debugging - catch (const exception& e) { - try { - wcerr << "ERROR: " << string_cast<wstring>(e.what(), CodePage::UTF8).c_str() - << endl; - fprintf(output(), "ERROR: %s\n", e.what()); - } catch (const exception& e) { - wcerr << "ERROR^2: " << string_cast<wstring>(e.what(), CodePage::UTF8).c_str() - << endl; - } catch (...) { - wcerr << "ERROR^2: unknown exception" << endl; - } - } catch (...) { - try { - wcerr << "ERROR: unknown exception" << endl; - fprintf(output(), "ERROR: unknown exception\n"); - } catch (const exception& e) { - wcerr << "ERROR^2: " << string_cast<wstring>(e.what(), CodePage::UTF8).c_str() - << endl; - } catch (...) { - wcerr << "ERROR^2: unknown exception" << endl; - } - } -#else - catch (bool) { - } -#endif - return 9; // exception -} - -void usvfs_test_base::log_settings(const std::wstring& exe_name) -{ - using namespace usvfs::shared; - fprintf(output(), "%s %s started with %s%s%s\n\n", - string_cast<std::string>(exe_name).c_str(), scenario_name(), - m_o.opsexe.filename().string().c_str(), m_o.ops_options.empty() ? "" : " ", - string_cast<std::string>(m_o.ops_options).c_str()); -} - -void usvfs_test_base::ops_list(const path& rel_path, bool recursive, bool with_contents, - bool should_succeed, const wstring& additional_args) -{ - wstring cmd = recursive ? L"-r -list" : L"-list"; - if (with_contents) - cmd += L"contents"; - run_ops(should_succeed, cmd, rel_path, additional_args); -} - -void usvfs_test_base::ops_read(const path& rel_path, bool should_succeed, - const wstring& additional_args) -{ - run_ops(should_succeed, L"-read", rel_path, additional_args); -} - -void usvfs_test_base::ops_rewrite(const path& rel_path, const char* contents, - bool should_succeed, const wstring& additional_args) -{ - using namespace usvfs::shared; - run_ops(should_succeed, L"-rewrite", rel_path, additional_args, - L"\"" + string_cast<wstring>(contents, CodePage::UTF8) + L"\""); -} - -void usvfs_test_base::ops_overwrite(const path& rel_path, const char* contents, - bool recursive, bool should_succeed, - const wstring& additional_args) -{ - using namespace usvfs::shared; - run_ops(should_succeed, recursive ? L"-r -overwrite" : L"-overwrite", rel_path, - additional_args, - L"\"" + string_cast<wstring>(contents, CodePage::UTF8) + L"\""); -} - -void usvfs_test_base::ops_touch(const path& rel_path, bool full_write_access, - bool should_succeed, const wstring& additional_args) -{ - run_ops(should_succeed, full_write_access ? L"-touchw" : L"-touch", rel_path, - additional_args); -} - -void usvfs_test_base::ops_deleteoverwrite(const path& rel_path, const char* contents, - bool recursive, bool should_succeed, - const wstring& additional_args) -{ - using namespace usvfs::shared; - run_ops(should_succeed, recursive ? L"-r -deleteoverwrite" : L"-deleteoverwrite", - rel_path, additional_args, - L"\"" + string_cast<wstring>(contents, CodePage::UTF8) + L"\""); -} - -void usvfs_test_base::ops_delete(const path& rel_path, bool should_succeed, - const wstring& additional_args) -{ - run_ops(should_succeed, L"-delete", rel_path, additional_args); -} - -void usvfs_test_base::ops_copy(const path& src_rel_path, const path& dest_rel_path, - bool replace, bool should_succeed, - const wstring& additional_args) -{ - run_ops(should_succeed, replace ? L"-copyover" : L"-copy", src_rel_path, - additional_args, wstring(), dest_rel_path); -} - -void usvfs_test_base::ops_rename(const path& src_rel_path, const path& dest_rel_path, - bool replace, bool allow_copy, bool should_succeed, - const wstring& additional_args) -{ - wstring command = allow_copy ? L"-move" : L"-rename"; - if (replace) - command += L"over"; - run_ops(should_succeed, command, src_rel_path, additional_args, wstring(), - dest_rel_path); -} - -void usvfs_test_base::ops_deleterename(const path& src_rel_path, - const path& dest_rel_path, bool allow_copy, - bool should_succeed, - const wstring& additional_args) -{ - wstring command = allow_copy ? L"-deletemove" : L"-deleterename"; - run_ops(should_succeed, command, src_rel_path, additional_args, wstring(), - dest_rel_path); -} - -void usvfs_test_base::run_ops(bool should_succeed, const wstring& preargs, - const path& rel_path, const wstring& additional_args, - const wstring& postargs, const path& rel_path2) -{ - using namespace usvfs::shared; - using string = std::string; - using wstring = wstring; - - string commandlog = test::path(m_o.opsexe).filename().string(); - wstring commandline = m_o.opsexe; - if (commandline.find(' ') != wstring::npos && - commandline.find('"') == wstring::npos) { - commandline = L"\"" + commandline + L"\""; - commandlog = "\"" + commandlog + "\""; - } - - if (!m_o.mount.empty()) { - commandline += L" -basedir "; - commandline += m_o.mount; - commandlog += " -basedir "; - commandlog += m_o.mount.filename().string(); - } - - if (!m_o.ops_options.empty()) { - commandline += L" "; - commandline += m_o.ops_options; - commandlog += " "; - commandlog += string_cast<string>(m_o.ops_options, CodePage::UTF8); - } - - commandline += L" -cout+ "; - commandline += m_o.output; - commandlog += " -cout+ "; - commandlog += m_o.output.filename().string(); - - if (!additional_args.empty()) { - commandline += L" "; - commandline += additional_args; - commandlog += " "; - commandlog += string_cast<string>(additional_args, CodePage::UTF8); - } - - if (!preargs.empty()) { - commandline += L" "; - commandline += preargs; - commandlog += " "; - commandlog += string_cast<string>(preargs, CodePage::UTF8); - } - - if (!rel_path.empty()) { - commandline += L" "; - commandline += m_o.mount / rel_path; - commandlog += " "; - commandlog += MOUNT_LABEL + rel_path.string(); - } - - if (!rel_path2.empty()) { - commandline += L" "; - commandline += m_o.mount / rel_path2; - commandlog += " "; - commandlog += MOUNT_LABEL + rel_path2.string(); - } - - if (!postargs.empty()) { - commandline += L" "; - commandline += postargs; - commandlog += " "; - commandlog += string_cast<string>(postargs, CodePage::UTF8); - } - - fprintf(output(), "Spawning: %s\n", commandlog.c_str()); - auto res = usvfs_connector::spawn(&commandline[0]); - fprintf(output(), "\n"); - - bool success = res == 0; - if (success != should_succeed) - throw test::FuncFailed("run_ops", success ? "succeeded" : "failed", - commandlog.c_str(), res); -} - -std::string usvfs_test_base::mount_contents(const path& rel_path) -{ - verify_mount_existance(rel_path); - const auto& contents = test::read_small_file(m_o.mount / rel_path); - return std::string(contents.data(), contents.size()); -} - -std::string usvfs_test_base::source_contents(const path& rel_path) -{ - verify_source_existance(rel_path); - const auto& contents = test::read_small_file(m_o.source / rel_path); - return std::string(contents.data(), contents.size()); -} - -void usvfs_test_base::verify_mount_contents(const path& rel_path, const char* contents) -{ - verify_mount_existance(rel_path); - if (verify_contents(m_o.mount / rel_path, contents)) - throw test::FuncFailed("verify_mount_contents", - (MOUNT_LABEL + rel_path.string()).c_str(), contents); -} - -void usvfs_test_base::verify_source_contents(const path& rel_path, const char* contents) -{ - verify_source_existance(rel_path); - if (verify_contents(m_o.source / rel_path, contents)) - throw test::FuncFailed("verify_source_contents", - (SOURCE_LABEL + rel_path.string()).c_str(), contents); -} - -bool usvfs_test_base::verify_contents(const path& file, const char* contents) -{ - // we allow difference in trailing whitespace (i.e. extra new line): - - size_t sz = strlen(contents); - while (sz && isspace(contents[sz - 1])) - --sz; - - const auto& real_contents = test::read_small_file(file); - size_t real_sz = real_contents.size(); - while (real_sz && isspace(real_contents[real_sz - 1])) - --real_sz; - - return sz == real_sz && memcmp(contents, real_contents.data(), sz); -} - -void usvfs_test_base::verify_mount_existance(const path& rel_path, bool exists, - bool is_dir) -{ - bool real_is_dir = false; - bool real_exists = - winapi::ex::wide::fileExists((m_o.mount / rel_path).c_str(), &real_is_dir); - if (exists != real_exists) - throw test::FuncFailed("verify_mount_existance", - real_exists ? "path exists" : "path does not exist", - (MOUNT_LABEL + rel_path.string()).c_str()); - else if (real_exists && is_dir != real_is_dir) - throw test::FuncFailed("verify_mount_existance", - real_is_dir ? "path is a directory" : "path is a file", - (MOUNT_LABEL + rel_path.string()).c_str()); -} - -void usvfs_test_base::verify_source_existance(const path& rel_path, bool exists, - bool is_dir) -{ - bool real_is_dir = false; - bool real_exists = - winapi::ex::wide::fileExists((m_o.source / rel_path).c_str(), &real_is_dir); - if (exists != real_exists) - throw test::FuncFailed("verify_source_existance", - real_exists ? "path exists" : "path does not exist", - (SOURCE_LABEL + rel_path.string()).c_str()); - else if (real_exists && is_dir != real_is_dir) - throw test::FuncFailed("verify_source_existance", - real_is_dir ? "path is a directory" : "path is a file", - (SOURCE_LABEL + rel_path.string()).c_str()); -} diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.h b/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.h deleted file mode 100644 index 4986e3b..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test/usvfs_test_base.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include <string> - -#include <test_helpers.h> - -class usvfs_test_options -{ -public: - static constexpr auto DEFAULT_MAPPING = L"vfs_mappings.txt"; - static constexpr auto MOUNT_DIR = L"mount"; - static constexpr auto SOURCE_DIR = L"source"; - - using path = test::path; - - // fills any values not set (or set to an empty value) to their default value - void fill_defaults(const path& test_name, const std::wstring& scenario, - const wchar_t* label); - - void set_ops32(); // sets opsexe iff opsexe is empty - void set_ops64(); // sets opsexe iff opsexe is empty - void add_ops_options(const std::wstring& options); - - path opsexe; - path fixture; - path mapping; - path temp; - path mount; - path source; - path output; - path usvfs_log; - std::wstring ops_options; - bool temp_cleanup = false; - bool force_temp_cleanup = false; -}; - -class usvfs_test_base -{ -public: - static constexpr auto MOUNT_DIR = usvfs_test_options::MOUNT_DIR; - static constexpr auto SOURCE_DIR = usvfs_test_options::SOURCE_DIR; - static constexpr auto MOUNT_LABEL = "mount:"; - static constexpr auto SOURCE_LABEL = "source:"; - static constexpr auto OUTPUT_CLEAN_SUFFIX = L"_clean"; - static constexpr auto POSTMORTEM_SUFFIX = L".postmortem"; - - using wstring = std::wstring; - using path = test::path; - - // options object should outlive this object. - usvfs_test_base(const usvfs_test_options& options) : m_o(options) {} - virtual ~usvfs_test_base() = default; - - int run(const std::wstring& exe_name); - - // function for override: - - virtual const char* scenario_name() = 0; - virtual bool scenario_run() = 0; - - // helpers for derived scenarios: - - virtual void ops_list(const path& rel_path, bool recursive, bool with_contents, - bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_read(const path& rel_path, bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_rewrite(const path& rel_path, const char* contents, - bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_overwrite(const path& rel_path, const char* contents, bool recursive, - bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_touch(const path& rel_path, bool full_write_access = false, - bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_deleteoverwrite(const path& rel_path, const char* contents, - bool recursive, bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_delete(const path& rel_path, bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_copy(const path& src_rel_path, const path& dest_rel_path, - bool replace, bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_rename(const path& src_rel_path, const path& dest_rel_path, - bool replace, bool allow_copy = false, - bool should_succeed = true, - const wstring& additional_args = wstring()); - virtual void ops_deleterename(const path& src_rel_path, const path& dest_rel_path, - bool allow_copy = false, bool should_succeed = true, - const wstring& additional_args = wstring()); - - virtual std::string mount_contents(const path& rel_path); - virtual void verify_mount_contents(const path& rel_path, const char* contents); - virtual void verify_mount_existance(const path& rel_path, bool exists = true, - bool is_dir = false); - virtual std::string source_contents(const path& rel_path); - virtual void verify_source_contents(const path& rel_path, const char* contents); - virtual void verify_source_existance(const path& rel_path, bool exists = true, - bool is_dir = false); - -private: - int run_impl(const std::wstring& exe_name); - void log_settings(const std::wstring& exe_name); - void cleanup_temp(); - void copy_fixture(); - bool postmortem_check(); - bool recursive_compare_dirs(path rel_path, path gold_base, path result_base, - FILE* log); - void clean_output(); - - test::ScopedFILE output(); - void run_ops(bool should_succeed, const wstring& preargs, const path& rel_path, - const wstring& additional_args, const wstring& postargs = wstring(), - const path& rel_path2 = path()); - bool verify_contents(const path& file, const char* contents); - - const usvfs_test_options& m_o; - bool m_clean_output = true; -}; diff --git a/libs/usvfs/test/usvfs_test_runner/usvfs_test_runner.cpp b/libs/usvfs/test/usvfs_test_runner/usvfs_test_runner.cpp deleted file mode 100644 index a70fb0f..0000000 --- a/libs/usvfs/test/usvfs_test_runner/usvfs_test_runner.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include <gtest/gtest.h> - -#include <boost/filesystem.hpp> -#include <iostream> -#include <test_helpers.h> -#include <windows_sane.h> - -static std::string usvfs_test_command(const char* scenario, const char* platform, - const char* testflag = nullptr, - const char* opsarg = nullptr) -{ - using namespace test; - std::string command = - path_of_test_bin(platform_dependant_executable("usvfs_test", "exe", platform)) - .string(); - if (testflag) { - command += " -"; - command += testflag; - } - if (opsarg) { - command += " -opsarg -"; - command += opsarg; - } - command += " "; - command += scenario; - if (testflag || opsarg) { - command += ":"; - if (testflag) { - command += testflag; - command += "_"; - } - if (opsarg) { - command += opsarg; - command += "_"; - } - command += platform; - } - return command; -} - -static DWORD spawn(std::string commandline) -{ - STARTUPINFOA si{0}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{0}; - - std::cout << "Running: [" << commandline << "]" << std::endl; - if (!CreateProcessA(NULL, commandline.data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, - &pi)) { - DWORD gle = GetLastError(); - std::cerr << "CreateProcess failed error=" << gle << std::endl; - return 98; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exit = 99; - if (!GetExitCodeProcess(pi.hProcess, &exit)) { - DWORD gle = GetLastError(); - std::cerr << "GetExitCodeProcess failed error=" << gle << std::endl; - } - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - return exit; -} - -TEST(UsvfsTest, basic_x64) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64"))); -} - -TEST(UsvfsTest, basic_x86) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86"))); -} - -TEST(UsvfsTest, basic_ops32_x64) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64", "ops32"))); -} - -TEST(UsvfsTest, basic_ops64_x86) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86", "ops64"))); -} - -/* -TEST(UsvfsTest, basic_ntapi_x64) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64", nullptr, "ntapi"))); -} - -TEST(UsvfsTest, basic_ntapi_x86) -{ - EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86", nullptr, "ntapi"))); -} -*/ - -int main(int argc, char** argv) -{ - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/libs/usvfs/vcpkg-configuration.json b/libs/usvfs/vcpkg-configuration.json deleted file mode 100644 index 723deee..0000000 --- a/libs/usvfs/vcpkg-configuration.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "default-registry": { - "kind": "git", - "repository": "https://github.com/Microsoft/vcpkg", - "baseline": "294f76666c3000630d828703e675814c05a4fd43" - }, - "registries": [ - { - "kind": "git", - "repository": "https://github.com/Microsoft/vcpkg", - "baseline": "294f76666c3000630d828703e675814c05a4fd43", - "packages": ["boost*", "boost-*"] - }, - { - "kind": "git", - "repository": "https://github.com/ModOrganizer2/vcpkg-registry", - "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60", - "packages": ["asmjit", "spdlog"] - } - ] -} diff --git a/libs/usvfs/vcpkg.json b/libs/usvfs/vcpkg.json deleted file mode 100644 index a0f9d7f..0000000 --- a/libs/usvfs/vcpkg.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "dependencies": [ - "asmjit", - "spdlog", - "libudis86", - "boost-algorithm", - "boost-any", - "boost-dll", - "boost-filesystem", - "boost-format", - "boost-interprocess", - "boost-headers", - "boost-locale", - "boost-multi-index", - "boost-thread", - "boost-predef" - ], - "features": { - "testing": { - "description": "Build USVFS tests.", - "dependencies": ["gtest"] - } - } -} |
