All unicode API functions are supposed to support paths longer than `MAX_PATH` if prepended with the extended-path prefix `\?`. As of writing this function in particular doesn't.
This is causing problems with Unreal Engine shader preprocessing when running under Wine.
From: Michael Stopa michael.stopa@tensorworks.com.au
All unicode API functions are supposed to support paths longer than `MAX_PATH` if prepended with the extended-path prefix `\?`. As of writing this function in particular doesn't in Wine. --- dlls/kernelbase/tests/file.c | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
diff --git a/dlls/kernelbase/tests/file.c b/dlls/kernelbase/tests/file.c index 18dff0a32ab..70fd6c6cd7e 100644 --- a/dlls/kernelbase/tests/file.c +++ b/dlls/kernelbase/tests/file.c @@ -24,11 +24,13 @@ #include <windef.h> #include <winbase.h> #include <winerror.h> +#include <winternl.h> #include <ioringapi.h>
#include "wine/test.h"
static HRESULT (WINAPI *pQueryIoRingCapabilities)(IORING_CAPABILITIES *); +static HRESULT (WINAPI *pGetFinalPathNameByHandleW)(HANDLE, WCHAR *, DWORD, DWORD);
static void test_ioring_caps(void) { @@ -46,12 +48,56 @@ static void test_ioring_caps(void) todo_wine ok(hr == S_OK, "got %#lx.\n", hr); }
+static void test_longpath_support(void) +{ + HANDLE file; + DWORD ret; + WCHAR dummy[128] = {}; + BOOL success; + /* Individual path components cannot be more than lpMaximumComponentLength as returned by + * GetVolumeInformation. This is typically only 255 characters, so testing long path support + * requires making a containing folder first */ + static const WCHAR testfolder[] = + L"\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + static const WCHAR testfile[] = + L"\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + if (!pGetFinalPathNameByHandleW) + { + win_skip("GetFinalPathNameByHandleW is missing\n"); + return; + } + + SetLastError(0xdeadbeef); + success = CreateDirectoryW(testfolder, NULL); + ok(success != 0, "CreateDirectoryW error %lu\n", GetLastError()); + + SetLastError(0xdeadbeef); + file = CreateFileW(testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_FLAG_DELETE_ON_CLOSE, 0); + ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetFinalPathNameByHandleW(file, dummy, 128, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + todo_wine ok(ret > 128, "GetFinalPathNameByHandleW returned %ld but expected more than 128, error %lu\n", + ret, GetLastError()); + + CloseHandle(file); + RemoveDirectoryW(testfolder); +} + START_TEST(file) { HMODULE hmod;
hmod = LoadLibraryA("kernelbase.dll"); pQueryIoRingCapabilities = (void *)GetProcAddress(hmod, "QueryIoRingCapabilities"); + pGetFinalPathNameByHandleW = (void *)GetProcAddress(hmod, "GetFinalPathNameByHandleW");
test_ioring_caps(); + test_longpath_support(); }
From: Michael Stopa michael.stopa@tensorworks.com.au
Like all unicode API functions, this one is supposed to support paths greater than `MAX_PATH` if prepended with `\?`. --- dlls/kernelbase/file.c | 53 +++++++++++++++++++++++++++++++----- dlls/kernelbase/tests/file.c | 2 +- 2 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 7d8b6844456..fea084e60c9 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -1777,12 +1777,13 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa DWORD count, DWORD flags ) { WCHAR buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH + 1]; - OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)&buffer; + OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)(void*)buffer; + WCHAR *buffer_ptr = 0; /* Used if extended path is needed */ WCHAR drive_part[MAX_PATH]; DWORD drive_part_len = 0; NTSTATUS status; DWORD result = 0; - ULONG dummy; + ULONG length = 0; WCHAR *ptr;
TRACE( "(%p,%p,%ld,%lx)\n", file, path, count, flags ); @@ -1795,12 +1796,35 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa }
/* get object name */ - status = NtQueryObject( file, ObjectNameInformation, &buffer, sizeof(buffer) - sizeof(WCHAR), &dummy ); - if (!set_ntstatus( status )) return 0; + status = NtQueryObject( file, ObjectNameInformation, buffer, sizeof(buffer), &length ); + if (status == STATUS_BUFFER_OVERFLOW) + { + if (length > sizeof(buffer)) + { + ULONG dummy = 0; + buffer_ptr = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, length ); + status = NtQueryObject( file, ObjectNameInformation, buffer_ptr, length, &dummy ); + if (!set_ntstatus( status )) + { + RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr); + return 0; + } + info = (OBJECT_NAME_INFORMATION*)(void*)buffer_ptr; + } else { + set_ntstatus( status ); + return 0; + } + } + else if (status != STATUS_SUCCESS) + { + set_ntstatus( status ); + return 0; + }
if (!info->Name.Buffer) { SetLastError( ERROR_INVALID_HANDLE ); + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); return 0; } if (info->Name.Length < 4 * sizeof(WCHAR) || info->Name.Buffer[0] != '\' || @@ -1808,6 +1832,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa { FIXME("Unexpected object name: %s\n", debugstr_wn(info->Name.Buffer, info->Name.Length / sizeof(WCHAR))); SetLastError( ERROR_GEN_FAILURE ); + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); return 0; }
@@ -1826,7 +1851,11 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa /* Get information required for VOLUME_NAME_NONE, VOLUME_NAME_GUID and VOLUME_NAME_NT */ if (flags == VOLUME_NAME_NONE || flags == VOLUME_NAME_GUID || flags == VOLUME_NAME_NT) { - if (!GetVolumePathNameW( info->Name.Buffer, drive_part, MAX_PATH )) return 0; + if (!GetVolumePathNameW( info->Name.Buffer, drive_part, MAX_PATH )) + { + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); + return 0; + } drive_part_len = lstrlenW(drive_part); if (!drive_part_len || drive_part_len > lstrlenW(info->Name.Buffer) || drive_part[drive_part_len-1] != '\' || @@ -1835,6 +1864,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa FIXME( "Path %s returned by GetVolumePathNameW does not match file path %s\n", debugstr_w(drive_part), debugstr_w(info->Name.Buffer) ); SetLastError( ERROR_GEN_FAILURE ); + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); return 0; } } @@ -1851,7 +1881,11 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa WCHAR volume_prefix[51];
/* GetVolumeNameForVolumeMountPointW sets error code on failure */ - if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) return 0; + if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) + { + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); + return 0; + } ptr = info->Name.Buffer + drive_part_len; result = lstrlenW(volume_prefix) + lstrlenW(ptr); if (result < count) @@ -1871,7 +1905,11 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa
/* QueryDosDeviceW sets error code on failure */ drive_part[drive_part_len - 1] = 0; - if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) return 0; + if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) + { + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); + return 0; + } ptr = info->Name.Buffer + drive_part_len - 1; result = lstrlenW(nt_prefix) + lstrlenW(ptr); if (result < count) @@ -1905,6 +1943,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR pa WARN("Invalid combination of flags: %lx\n", flags); SetLastError( ERROR_INVALID_PARAMETER ); } + if ( buffer_ptr ) RtlFreeHeap( GetProcessHeap(), 0, buffer_ptr ); return result; }
diff --git a/dlls/kernelbase/tests/file.c b/dlls/kernelbase/tests/file.c index 70fd6c6cd7e..5c83cb00805 100644 --- a/dlls/kernelbase/tests/file.c +++ b/dlls/kernelbase/tests/file.c @@ -83,7 +83,7 @@ static void test_longpath_support(void)
SetLastError(0xdeadbeef); ret = pGetFinalPathNameByHandleW(file, dummy, 128, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); - todo_wine ok(ret > 128, "GetFinalPathNameByHandleW returned %ld but expected more than 128, error %lu\n", + ok(ret > 128, "GetFinalPathNameByHandleW returned %ld but expected more than 128, error %lu\n", ret, GetLastError());
CloseHandle(file);