[PATCH v2 0/4] MR10247: server, ntoskrnl.exe: Implement FsRtlGetFileSize().
Needed by Persona 5: The Phantom X (SEA/KR/CN) in order to launch the game. -- v2: ntoskrnl.exe/tests: Add tests for FsRtlGetFileSize(). ntoskrnl.exe: Implement FsRtlGetFileSize(). ntoskrnl.exe/tests: Add tests for directory kernel objects. server: Allow creating directory kernel objects. https://gitlab.winehq.org/wine/wine/-/merge_requests/10247
From: Nello De Gregoris <bluechxindv@gmail.com> Based on https://gitlab.winehq.org/wine/wine/-/commit/dcaeddd4db127ebabc4f27627e1278b.... --- server/change.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/change.c b/server/change.c index 5bcd0676e0d..e1c77218e46 100644 --- a/server/change.c +++ b/server/change.c @@ -94,6 +94,7 @@ struct dir struct inode *inode; /* inode of the associated directory */ struct process *client_process; /* client process that has a cache for this directory */ int client_entry; /* entry in client process cache */ + struct list kernel_object; /* list of kernel object pointers */ }; static struct fd *dir_get_fd( struct object *obj ); @@ -103,6 +104,7 @@ static int dir_set_sd( struct object *obj, const struct security_descriptor *sd, static void dir_dump( struct object *obj, int verbose ); static int dir_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); static void dir_destroy( struct object *obj ); +static struct list *dir_get_kernel_obj_list( struct object *obj ); static const struct object_ops dir_ops = { @@ -124,7 +126,7 @@ static const struct object_ops dir_ops = no_link_name, /* link_name */ NULL, /* unlink_name */ no_open_file, /* open_file */ - no_kernel_obj_list, /* get_kernel_obj_list */ + dir_get_kernel_obj_list, /* get_kernel_obj_list */ dir_close_handle, /* close_handle */ dir_destroy /* destroy */ }; @@ -452,6 +454,12 @@ static void dir_destroy( struct object *obj ) } } +static struct list *dir_get_kernel_obj_list( struct object *obj ) +{ + struct dir *dir = (struct dir *)obj; + return &dir->kernel_object; +} + struct dir *get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access ) { return (struct dir *)get_handle_obj( process, handle, access, &dir_ops ); @@ -1143,6 +1151,7 @@ struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode ) return NULL; list_init( &dir->change_records ); + list_init( &dir->kernel_object ); dir->filter = 0; dir->notified = 0; dir->want_data = 0; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10247
From: Nello De Gregoris <bluechxindv@gmail.com> --- dlls/ntoskrnl.exe/tests/driver.c | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 9358cf605a4..12fc1b8fc0a 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -1981,6 +1981,40 @@ static void test_object_name(void) ok(!name->Name.MaximumLength, "got maximum length %u\n", name->Name.MaximumLength); } +static void test_dir_kernel_object(void) +{ + OBJECT_ATTRIBUTES attr = { sizeof(attr) }; + UNICODE_STRING pathU; + IO_STATUS_BLOCK io; + FILE_OBJECT *file_obj; + HANDLE dir_handle, handle; + NTSTATUS status; + + RtlInitUnicodeString(&pathU, L"\\??\\C:\\windows"); + InitializeObjectAttributes(&attr, &pathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwOpenFile(&dir_handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); + ok(!status, "ZwOpenFile failed: %#lx\n", status); + if (status) + return; + + status = ObReferenceObjectByHandle(dir_handle, 0, *pIoFileObjectType, KernelMode, + (void **)&file_obj, NULL); + ok(!status, "ObReferenceObjectByHandle failed: %#lx\n", status); + if (!status) + { + status = ObOpenObjectByPointer(file_obj, OBJ_KERNEL_HANDLE, NULL, 0, + *pIoFileObjectType, KernelMode, &handle); + ok(!status, "ObOpenObjectByPointer failed: %#lx\n", status); + if (!status) + ZwClose(handle); + ObDereferenceObject(file_obj); + } + + ZwClose(dir_handle); +} + static PIO_WORKITEM work_item; static void WINAPI main_test_task(DEVICE_OBJECT *device, void *context) @@ -2523,6 +2557,7 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st test_lookup_thread(); test_IoAttachDeviceToDeviceStack(); test_object_name(); + test_dir_kernel_object(); #if defined(__i386__) || defined(__x86_64__) test_executable_pool(); #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10247
From: Nello De Gregoris <bluechxindv@gmail.com> --- dlls/ntoskrnl.exe/ntoskrnl.c | 25 +++++++++++++++++++++++++ dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- include/ddk/ntifs.h | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 0855749249f..adf5a9bcd3f 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -2460,6 +2460,31 @@ NTSTATUS WINAPI ExInitializeZone(PZONE_HEADER Zone, return STATUS_NOT_IMPLEMENTED; } +/*********************************************************************** + * FsRtlGetFileSize (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI FsRtlGetFileSize( PFILE_OBJECT file_obj, PLARGE_INTEGER file_size ) +{ + FILE_STANDARD_INFORMATION info; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + HANDLE handle; + + TRACE( "file_obj %p, file_size %p\n", file_obj, file_size ); + + status = ObOpenObjectByPointer( file_obj, 0, NULL, 0, IoFileObjectType, KernelMode, &handle ); + if (status) return status; + + status = NtQueryInformationFile( handle, &iosb, &info, sizeof(info), FileStandardInformation ); + NtClose( handle ); + if (!status) + { + if (info.Directory) return STATUS_FILE_IS_A_DIRECTORY; + file_size->QuadPart = info.EndOfFile.QuadPart; + } + return status; +} + /*********************************************************************** * FsRtlIsNameInExpression (NTOSKRNL.EXE.@) */ diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index f4699c6c65a..17c74b9322e 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -237,7 +237,7 @@ @ stub FsRtlFastUnlockSingle @ stub FsRtlFindInTunnelCache @ stub FsRtlFreeFileLock -@ stub FsRtlGetFileSize +@ stdcall FsRtlGetFileSize(ptr ptr) @ stub FsRtlGetNextFileLock @ stub FsRtlGetNextLargeMcbEntry @ stub FsRtlGetNextMcbEntry diff --git a/include/ddk/ntifs.h b/include/ddk/ntifs.h index 980235abdc9..965715738d7 100644 --- a/include/ddk/ntifs.h +++ b/include/ddk/ntifs.h @@ -203,6 +203,7 @@ typedef struct _REPARSE_GUID_DATA_BUFFER #define COMPRESSION_FORMAT_MASK 0x00ff #define COMPRESSION_ENGINE_MASK 0xff00 +NTSTATUS WINAPI FsRtlGetFileSize(PFILE_OBJECT, PLARGE_INTEGER); BOOLEAN WINAPI FsRtlIsNameInExpression(PUNICODE_STRING, PUNICODE_STRING, BOOLEAN, PWCH); DEVICE_OBJECT * WINAPI IoGetAttachedDevice(DEVICE_OBJECT*); PEPROCESS WINAPI IoGetRequestorProcess(IRP*); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10247
From: Nello De Gregoris <bluechxindv@gmail.com> --- dlls/ntoskrnl.exe/tests/driver.c | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 12fc1b8fc0a..030c6476a52 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -2015,6 +2015,71 @@ static void test_dir_kernel_object(void) ZwClose(dir_handle); } +static void test_fsrtl_get_file_size(void) +{ + static const char data[] = "hello, world!"; + OBJECT_ATTRIBUTES attr = { sizeof(attr) }; + UNICODE_STRING pathU; + IO_STATUS_BLOCK io; + LARGE_INTEGER file_size, offset; + HANDLE file_handle, dir_handle; + FILE_OBJECT *file_obj, *dir_obj; + NTSTATUS status; + + /* test regular file */ + RtlInitUnicodeString(&pathU, L"\\??\\C:\\windows\\winetest_ntoskrnl_fsrtl.tmp"); + InitializeObjectAttributes(&attr, &pathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwCreateFile(&file_handle, DELETE | FILE_WRITE_DATA | SYNCHRONIZE, &attr, &io, NULL, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + ok(!status, "ZwCreateFile failed: %#lx\n", status); + if (status) + return; + + offset.QuadPart = 0; + status = ZwWriteFile(file_handle, NULL, NULL, NULL, &io, (void *)data, sizeof(data), &offset, NULL); + ok(!status, "ZwWriteFile failed: %#lx\n", status); + + status = ObReferenceObjectByHandle(file_handle, 0, *pIoFileObjectType, KernelMode, + (void **)&file_obj, NULL); + ok(!status, "ObReferenceObjectByHandle failed: %#lx\n", status); + if (!status) + { + file_size.QuadPart = 0; + status = FsRtlGetFileSize(file_obj, &file_size); + ok(!status, "FsRtlGetFileSize failed: %#lx\n", status); + ok(file_size.QuadPart == sizeof(data), "expected %Iu, got %I64d\n", + sizeof(data), file_size.QuadPart); + ObDereferenceObject(file_obj); + } + + ZwClose(file_handle); + + /* test directory returns STATUS_FILE_IS_A_DIRECTORY */ + RtlInitUnicodeString(&pathU, L"\\??\\C:\\windows"); + InitializeObjectAttributes(&attr, &pathU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwOpenFile(&dir_handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); + ok(!status, "ZwOpenFile failed: %#lx\n", status); + if (status) + return; + + status = ObReferenceObjectByHandle(dir_handle, 0, *pIoFileObjectType, KernelMode, + (void **)&dir_obj, NULL); + ok(!status, "ObReferenceObjectByHandle failed: %#lx\n", status); + if (!status) + { + file_size.QuadPart = 0; + status = FsRtlGetFileSize(dir_obj, &file_size); + ok(status == STATUS_FILE_IS_A_DIRECTORY, + "expected STATUS_FILE_IS_A_DIRECTORY, got %#lx\n", status); + ObDereferenceObject(dir_obj); + } + + ZwClose(dir_handle); +} + static PIO_WORKITEM work_item; static void WINAPI main_test_task(DEVICE_OBJECT *device, void *context) @@ -2558,6 +2623,7 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st test_IoAttachDeviceToDeviceStack(); test_object_name(); test_dir_kernel_object(); + test_fsrtl_get_file_size(); #if defined(__i386__) || defined(__x86_64__) test_executable_pool(); #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10247
v2: Added tests for directory kernel objects. Many thanks for looking into this! Please let me know if I can improve the tests in any way :pray: -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131379
Vishnunithyasoundhar S (@svishnunithyasoundhar) commented about dlls/ntoskrnl.exe/ntoskrnl.c:
return STATUS_NOT_IMPLEMENTED; }
+/*********************************************************************** + * FsRtlGetFileSize (NTOSKRNL.EXE.@) + */ +NTSTATUS WINAPI FsRtlGetFileSize( PFILE_OBJECT file_obj, PLARGE_INTEGER file_size ) +{ + FILE_STANDARD_INFORMATION info; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + HANDLE handle; + + TRACE( "file_obj %p, file_size %p\n", file_obj, file_size ); + + status = ObOpenObjectByPointer( file_obj, 0, NULL, 0, IoFileObjectType, KernelMode, &handle );
I think it would be good if you have a basic check to see if `file_obj` is a null pointer before dereferencing it. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131380
On Thu Mar 5 19:54:05 2026 +0000, Vishnunithyasoundhar S wrote:
I think it would be good if you have a basic check to see if `file_obj` is a null pointer before dereferencing it. this would need a test to check if windows would crash
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131390
On Thu Mar 5 22:02:52 2026 +0000, Etaash Mathamsetty wrote:
this would need a test to check if windows would crash From my testing Windows does indeed crash in such case, check is not needed.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131401
On Thu Mar 5 22:02:52 2026 +0000, Nello De Gregoris wrote:
From my testing Windows does indeed crash in such case, check is not needed. Also, I don't think there's any reason to try to test for and anticipate this beforehand. If an application really depends on Windows handling things like this, it'll be immediately obvious on debugging, and on the other hand the rarity of which applications actually do depend on this means that testing for it is a waste of time.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131403
On Thu Mar 5 22:29:58 2026 +0000, Elizabeth Figura wrote:
Also, I don't think there's any reason to try to test for and anticipate this beforehand. If an application really depends on Windows handling things like this, it'll be immediately obvious on debugging, and on the other hand the rarity of which applications actually do depend on this means that testing for it is a waste of time. yeah I agree, I meant just test locally to see if it does crash or not
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131405
yeah I agree, I meant just test locally to see if it does crash or not
I think even this is a waste of time. Especially when we're talking about testing kernel APIs. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10247#note_131417
participants (5)
-
Elizabeth Figura (@zfigura) -
Etaash Mathamsetty (@etaash.mathamsetty) -
Nello De Gregoris -
Nello De Gregoris (@bluechxin) -
Vishnunithyasoundhar S (@svishnunithyasoundhar)