mirror of
https://github.com/ModOrganizer2/usvfs.git
synced 2026-07-27 14:04:27 -07:00
Use proper structure for FileFullDirectoryInformation in NTDLL hooks. (#84)
This commit is contained in:
@@ -41,9 +41,6 @@ DECLARE_HAS_FIELD(ShortName)
|
||||
template <FILE_INFORMATION_CLASS fileInformationClass, class FileInformationClass>
|
||||
struct FileInformationClassUtilsImpl
|
||||
{
|
||||
// minimum required size for the structure
|
||||
std::size_t get_minimum_struct_size() { return sizeof(FileInformationClass); }
|
||||
|
||||
static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName)
|
||||
{
|
||||
const FileInformationClass* info =
|
||||
@@ -101,7 +98,7 @@ struct FileInformationClassUtils<FileDirectoryInformation>
|
||||
template <>
|
||||
struct FileInformationClassUtils<FileFullDirectoryInformation>
|
||||
: FileInformationClassUtilsImpl<FileFullDirectoryInformation,
|
||||
FILE_ID_FULL_DIR_INFORMATION>
|
||||
FILE_FULL_DIR_INFORMATION>
|
||||
{};
|
||||
template <>
|
||||
struct FileInformationClassUtils<FileBothDirectoryInformation>
|
||||
@@ -125,10 +122,6 @@ struct FileInformationClassUtils<FileNamesInformation>
|
||||
: FileInformationClassUtilsImpl<FileNamesInformation, FILE_NAMES_INFORMATION>
|
||||
{};
|
||||
template <>
|
||||
struct FileInformationClassUtils<FileAllInformation>
|
||||
: FileInformationClassUtilsImpl<FileAllInformation, FILE_ALL_INFORMATION>
|
||||
{};
|
||||
template <>
|
||||
struct FileInformationClassUtils<FileObjectIdInformation>
|
||||
: FileInformationClassUtilsImpl<FileObjectIdInformation, FILE_OBJECTID_INFORMATION>
|
||||
{};
|
||||
@@ -183,6 +176,32 @@ struct FileInformationClassUtils<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, ...) \
|
||||
@@ -190,6 +209,7 @@ struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
|
||||
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__) \
|
||||
@@ -197,7 +217,6 @@ struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
|
||||
_APP_FINFO_CASE(FileNameInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileRenameInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileNamesInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileAllInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileObjectIdInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileReparsePointInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileIdBothDirectoryInformation, fn, __VA_ARGS__) \
|
||||
@@ -210,17 +229,6 @@ struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
|
||||
_APP_FINFO_CASE(FileIdAllExtdDirectoryInformation, fn, __VA_ARGS__) \
|
||||
_APP_FINFO_CASE(FileIdAllExtdBothDirectoryInformation, fn, __VA_ARGS__)
|
||||
|
||||
// minimum required size for the structure
|
||||
// std::size_t
|
||||
// get_file_information_minimum_struct_size(FILE_INFORMATION_CLASS fileInformationClass)
|
||||
// {
|
||||
// switch (fileInformationClass) {
|
||||
// _APPLY_FILEINFO_FN(get_minimum_struct_size, );
|
||||
// default:
|
||||
// return 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
void GetFileInformationData(FILE_INFORMATION_CLASS fileInformationClass,
|
||||
LPCVOID address, ULONG& offset, std::wstring& fileName)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ 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::gtest_main)
|
||||
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
|
||||
|
||||
+54
-7
@@ -25,12 +25,8 @@ along with usvfs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <test_helpers.h>
|
||||
|
||||
#pragma warning(push, 3)
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <fstream>
|
||||
#pragma warning(pop)
|
||||
#include <iostream>
|
||||
|
||||
#include <inject.h>
|
||||
#include <stringutils.h>
|
||||
@@ -47,6 +43,9 @@ along with usvfs. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <unicodestring.h>
|
||||
#include <usvfs.h>
|
||||
|
||||
#include <gmock/gmock-matchers.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace spd = spdlog;
|
||||
|
||||
namespace ush = usvfs::shared;
|
||||
@@ -299,7 +298,7 @@ HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess
|
||||
string.Buffer = stringBuffer;
|
||||
lstrcpyW(stringBuffer, L"\\??\\");
|
||||
lstrcatW(stringBuffer, path);
|
||||
string.Length = lstrlenW(stringBuffer) * 2;
|
||||
string.Length = static_cast<USHORT>(lstrlenW(stringBuffer) * 2);
|
||||
string.MaximumLength = BUFFER_SIZE;
|
||||
attributes.ObjectName = &string;
|
||||
|
||||
@@ -365,6 +364,53 @@ TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile)
|
||||
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;
|
||||
@@ -405,6 +451,7 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile)
|
||||
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 =
|
||||
@@ -471,7 +518,7 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile)
|
||||
{
|
||||
// expected length is sizeof struct + size of path (in bytes), including the
|
||||
// null-character
|
||||
const ULONG expectedLength =
|
||||
const auto expectedLength =
|
||||
sizeof(OBJECT_NAME_INFORMATION) + c_drive_device.size() * 2 + 12 + 2;
|
||||
ULONG requiredLength;
|
||||
NTSTATUS res;
|
||||
|
||||
Reference in New Issue
Block a user