Skip to content

Commit de356fb

Browse files
authored
Use proper structure for FileFullDirectoryInformation in NTDLL hooks. (#84)
1 parent eeee538 commit de356fb

3 files changed

Lines changed: 83 additions & 28 deletions

File tree

src/usvfs_dll/hooks/file_information_utils.h

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ DECLARE_HAS_FIELD(ShortName)
4141
template <FILE_INFORMATION_CLASS fileInformationClass, class FileInformationClass>
4242
struct FileInformationClassUtilsImpl
4343
{
44-
// minimum required size for the structure
45-
std::size_t get_minimum_struct_size() { return sizeof(FileInformationClass); }
46-
4744
static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName)
4845
{
4946
const FileInformationClass* info =
@@ -101,7 +98,7 @@ struct FileInformationClassUtils<FileDirectoryInformation>
10198
template <>
10299
struct FileInformationClassUtils<FileFullDirectoryInformation>
103100
: FileInformationClassUtilsImpl<FileFullDirectoryInformation,
104-
FILE_ID_FULL_DIR_INFORMATION>
101+
FILE_FULL_DIR_INFORMATION>
105102
{};
106103
template <>
107104
struct FileInformationClassUtils<FileBothDirectoryInformation>
@@ -125,10 +122,6 @@ struct FileInformationClassUtils<FileNamesInformation>
125122
: FileInformationClassUtilsImpl<FileNamesInformation, FILE_NAMES_INFORMATION>
126123
{};
127124
template <>
128-
struct FileInformationClassUtils<FileAllInformation>
129-
: FileInformationClassUtilsImpl<FileAllInformation, FILE_ALL_INFORMATION>
130-
{};
131-
template <>
132125
struct FileInformationClassUtils<FileObjectIdInformation>
133126
: FileInformationClassUtilsImpl<FileObjectIdInformation, FILE_OBJECTID_INFORMATION>
134127
{};
@@ -183,21 +176,47 @@ struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
183176
FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION>
184177
{};
185178

179+
// FILE_ALL_INFORMATION needs to be handled differently because it has a field that
180+
// is itself a structure
181+
template <>
182+
struct FileInformationClassUtils<FileAllInformation>
183+
{
184+
static void get_data(LPCVOID address, ULONG& offset, std::wstring& fileName)
185+
{
186+
FileInformationClassUtils<FileNameInformation>::get_data(
187+
&reinterpret_cast<const FILE_ALL_INFORMATION*>(address)->NameInformation,
188+
offset, fileName);
189+
}
190+
191+
static void set_offset(LPVOID address, ULONG offset)
192+
{
193+
// this is a no-op but it's consistent to do that everywhere
194+
FileInformationClassUtils<FileNameInformation>::set_offset(
195+
&reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, offset);
196+
}
197+
198+
static void set_filename(LPVOID address, const std::wstring& fileName)
199+
{
200+
FileInformationClassUtils<FileNameInformation>::set_filename(
201+
&reinterpret_cast<FILE_ALL_INFORMATION*>(address)->NameInformation, fileName);
202+
}
203+
};
204+
186205
} // namespace usvfs::details
187206

188207
#define _APP_FINFO_CASE(clazz, fn, ...) \
189208
case clazz: \
190209
return usvfs::details::FileInformationClassUtils<clazz>::fn(__VA_ARGS__);
191210

192211
#define _APPLY_FILEINFO_FN(fn, ...) \
212+
_APP_FINFO_CASE(FileAllInformation, fn, __VA_ARGS__) \
193213
_APP_FINFO_CASE(FileDirectoryInformation, fn, __VA_ARGS__) \
194214
_APP_FINFO_CASE(FileFullDirectoryInformation, fn, __VA_ARGS__) \
195215
_APP_FINFO_CASE(FileBothDirectoryInformation, fn, __VA_ARGS__) \
196216
_APP_FINFO_CASE(FileStandardInformation, fn, __VA_ARGS__) \
197217
_APP_FINFO_CASE(FileNameInformation, fn, __VA_ARGS__) \
198218
_APP_FINFO_CASE(FileRenameInformation, fn, __VA_ARGS__) \
199219
_APP_FINFO_CASE(FileNamesInformation, fn, __VA_ARGS__) \
200-
_APP_FINFO_CASE(FileAllInformation, fn, __VA_ARGS__) \
201220
_APP_FINFO_CASE(FileObjectIdInformation, fn, __VA_ARGS__) \
202221
_APP_FINFO_CASE(FileReparsePointInformation, fn, __VA_ARGS__) \
203222
_APP_FINFO_CASE(FileIdBothDirectoryInformation, fn, __VA_ARGS__) \
@@ -210,17 +229,6 @@ struct FileInformationClassUtils<FileIdAllExtdBothDirectoryInformation>
210229
_APP_FINFO_CASE(FileIdAllExtdDirectoryInformation, fn, __VA_ARGS__) \
211230
_APP_FINFO_CASE(FileIdAllExtdBothDirectoryInformation, fn, __VA_ARGS__)
212231

213-
// minimum required size for the structure
214-
// std::size_t
215-
// get_file_information_minimum_struct_size(FILE_INFORMATION_CLASS fileInformationClass)
216-
// {
217-
// switch (fileInformationClass) {
218-
// _APPLY_FILEINFO_FN(get_minimum_struct_size, );
219-
// default:
220-
// return 0;
221-
// }
222-
// }
223-
224232
void GetFileInformationData(FILE_INFORMATION_CLASS fileInformationClass,
225233
LPCVOID address, ULONG& offset, std::wstring& fileName)
226234
{

test/tvfs_test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ find_package(GTest CONFIG REQUIRED)
44

55
add_executable(tvfs_test main.cpp)
66
usvfs_set_test_properties(tvfs_test)
7-
target_link_libraries(tvfs_test PRIVATE test_utils usvfs_helper GTest::gtest GTest::gtest_main)
7+
target_link_libraries(tvfs_test PRIVATE test_utils usvfs_helper GTest::gtest GTest::gmock GTest::gtest_main)
88
usvfs_target_link_usvfs(tvfs_test)
99

1010
# tvfs_test uses a private USVFS header so we need to include it manually

test/tvfs_test/main.cpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@ along with usvfs. If not, see <http://www.gnu.org/licenses/>.
2525

2626
#include <test_helpers.h>
2727

28-
#pragma warning(push, 3)
29-
#include <gtest/gtest.h>
30-
#include <iostream>
31-
3228
#include <fstream>
33-
#pragma warning(pop)
29+
#include <iostream>
3430

3531
#include <inject.h>
3632
#include <stringutils.h>
@@ -47,6 +43,9 @@ along with usvfs. If not, see <http://www.gnu.org/licenses/>.
4743
#include <unicodestring.h>
4844
#include <usvfs.h>
4945

46+
#include <gmock/gmock-matchers.h>
47+
#include <gtest/gtest.h>
48+
5049
namespace spd = spdlog;
5150

5251
namespace ush = usvfs::shared;
@@ -299,7 +298,7 @@ HANDLE hooked_NtOpenFile(LPCWSTR path, ACCESS_MASK accessMask, ULONG shareAccess
299298
string.Buffer = stringBuffer;
300299
lstrcpyW(stringBuffer, L"\\??\\");
301300
lstrcatW(stringBuffer, path);
302-
string.Length = lstrlenW(stringBuffer) * 2;
301+
string.Length = static_cast<USHORT>(lstrlenW(stringBuffer) * 2);
303302
string.MaximumLength = BUFFER_SIZE;
304303
attributes.ObjectName = &string;
305304

@@ -365,6 +364,53 @@ TEST_F(USVFSTest, NtQueryDirectoryFileFindsVirtualFile)
365364
usvfs::hook_NtClose(hdl);
366365
}
367366

367+
TEST_F(USVFSTest, NtQueryDirectoryFileExVirtualFile)
368+
{
369+
auto params = defaultUsvfsParams();
370+
std::unique_ptr<usvfs::HookContext> ctx(
371+
usvfsCreateHookContext(*params, ::GetModuleHandle(nullptr)));
372+
usvfs::RedirectionTreeContainer& tree = ctx->redirectionTable();
373+
374+
tree.addFile(L"C:\\0123456789.txt", usvfs::RedirectionDataLocal(REAL_FILEA));
375+
tree.addFile(L"C:\\123456", usvfs::RedirectionDataLocal(REAL_FILEA));
376+
tree.addFile(L"C:\\abcdef", usvfs::RedirectionDataLocal(REAL_FILEA));
377+
tree.addFile(L"C:\\abcdefghijklmnopqrstuvwxyz.txt",
378+
usvfs::RedirectionDataLocal(REAL_FILEA));
379+
380+
HANDLE hdl =
381+
hooked_NtOpenFile(L"C:\\", FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
382+
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
383+
ASSERT_NE(INVALID_HANDLE_VALUE, hdl);
384+
385+
IO_STATUS_BLOCK status;
386+
387+
constexpr size_t BUFFER_SIZE = 2048;
388+
char buffer[BUFFER_SIZE];
389+
390+
std::vector<std::wstring> foundFiles;
391+
while (usvfs::hook_NtQueryDirectoryFileEx(
392+
hdl, nullptr, nullptr, nullptr, &status, buffer, BUFFER_SIZE,
393+
FileFullDirectoryInformation, 0, nullptr) == STATUS_SUCCESS) {
394+
std::size_t offset = 0;
395+
while (offset < BUFFER_SIZE) {
396+
const auto* info = reinterpret_cast<FILE_FULL_DIR_INFORMATION*>(buffer + offset);
397+
foundFiles.emplace_back(info->FileName, info->FileNameLength / sizeof(wchar_t));
398+
399+
if (info->NextEntryOffset == 0) {
400+
break; // no more entries
401+
}
402+
403+
offset += info->NextEntryOffset;
404+
}
405+
}
406+
407+
ASSERT_THAT(foundFiles,
408+
::testing::IsSupersetOf({L"0123456789.txt", L"123456", L"abcdef",
409+
L"abcdefghijklmnopqrstuvwxyz.txt"}));
410+
411+
usvfs::hook_NtClose(hdl);
412+
}
413+
368414
TEST_F(USVFSTest, NtQueryObjectVirtualFile)
369415
{
370416
std::wstring c_drive_device;
@@ -405,6 +451,7 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile)
405451
IO_STATUS_BLOCK status;
406452
const auto res = usvfs::hook_NtQueryInformationFile(
407453
hdl, &status, buffer, sizeof(buffer), FileNameInformation);
454+
ASSERT_EQ(STATUS_SUCCESS, res);
408455
ASSERT_EQ(STATUS_SUCCESS, status.Status);
409456

410457
FILE_NAME_INFORMATION* fileNameInfo =
@@ -471,7 +518,7 @@ TEST_F(USVFSTest, NtQueryObjectVirtualFile)
471518
{
472519
// expected length is sizeof struct + size of path (in bytes), including the
473520
// null-character
474-
const ULONG expectedLength =
521+
const auto expectedLength =
475522
sizeof(OBJECT_NAME_INFORMATION) + c_drive_device.size() * 2 + 12 + 2;
476523
ULONG requiredLength;
477524
NTSTATUS res;

0 commit comments

Comments
 (0)