From 6e12aba9c7f0fef8d2b1b7202ad778c399e6e862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller?= Date: Thu, 7 Apr 2016 10:28:52 -0500 Subject: [PATCH] kernel32: Implement GetFinalPathNameByHandle. Signed-off-by: Andrew Eikum Signed-off-by: Alexandre Julliard --- .../api-ms-win-core-file-l1-1-0.spec | 4 +- .../api-ms-win-core-file-l1-2-0.spec | 4 +- .../api-ms-win-core-file-l1-2-1.spec | 4 +- dlls/kernel32/file.c | 211 ++++++++++++++++++ dlls/kernel32/kernel32.spec | 4 +- dlls/kernel32/tests/file.c | 40 ++++ dlls/kernelbase/kernelbase.spec | 4 +- 7 files changed, 261 insertions(+), 10 deletions(-) diff --git a/dlls/api-ms-win-core-file-l1-1-0/api-ms-win-core-file-l1-1-0.spec b/dlls/api-ms-win-core-file-l1-1-0/api-ms-win-core-file-l1-1-0.spec index 9e5b809290a..61e8038083e 100644 --- a/dlls/api-ms-win-core-file-l1-1-0/api-ms-win-core-file-l1-1-0.spec +++ b/dlls/api-ms-win-core-file-l1-1-0/api-ms-win-core-file-l1-1-0.spec @@ -39,8 +39,8 @@ @ stdcall GetFileSizeEx(long ptr) kernel32.GetFileSizeEx @ stdcall GetFileTime(long ptr ptr ptr) kernel32.GetFileTime @ stdcall GetFileType(long) kernel32.GetFileType -@ stub GetFinalPathNameByHandleA -@ stub GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) kernel32.GetFinalPathNameByHandleA +@ stdcall GetFinalPathNameByHandleW(long ptr long long) kernel32.GetFinalPathNameByHandleW @ stdcall GetFullPathNameA(str long ptr ptr) kernel32.GetFullPathNameA @ stdcall GetFullPathNameW(wstr long ptr ptr) kernel32.GetFullPathNameW @ stdcall GetLogicalDriveStringsW(long ptr) kernel32.GetLogicalDriveStringsW diff --git a/dlls/api-ms-win-core-file-l1-2-0/api-ms-win-core-file-l1-2-0.spec b/dlls/api-ms-win-core-file-l1-2-0/api-ms-win-core-file-l1-2-0.spec index ebfd52ec193..cddf1129f43 100644 --- a/dlls/api-ms-win-core-file-l1-2-0/api-ms-win-core-file-l1-2-0.spec +++ b/dlls/api-ms-win-core-file-l1-2-0/api-ms-win-core-file-l1-2-0.spec @@ -39,8 +39,8 @@ @ stdcall GetFileSizeEx(long ptr) kernel32.GetFileSizeEx @ stdcall GetFileTime(long ptr ptr ptr) kernel32.GetFileTime @ stdcall GetFileType(long) kernel32.GetFileType -@ stub GetFinalPathNameByHandleA -@ stub GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) kernel32.GetFinalPathNameByHandleA +@ stdcall GetFinalPathNameByHandleW(long ptr long long) kernel32.GetFinalPathNameByHandleW @ stdcall GetFullPathNameA(str long ptr ptr) kernel32.GetFullPathNameA @ stdcall GetFullPathNameW(wstr long ptr ptr) kernel32.GetFullPathNameW @ stdcall GetLogicalDriveStringsW(long ptr) kernel32.GetLogicalDriveStringsW diff --git a/dlls/api-ms-win-core-file-l1-2-1/api-ms-win-core-file-l1-2-1.spec b/dlls/api-ms-win-core-file-l1-2-1/api-ms-win-core-file-l1-2-1.spec index c7691a4f6e9..a9acd8deb47 100644 --- a/dlls/api-ms-win-core-file-l1-2-1/api-ms-win-core-file-l1-2-1.spec +++ b/dlls/api-ms-win-core-file-l1-2-1/api-ms-win-core-file-l1-2-1.spec @@ -41,8 +41,8 @@ @ stdcall GetFileSizeEx(long ptr) kernel32.GetFileSizeEx @ stdcall GetFileTime(long ptr ptr ptr) kernel32.GetFileTime @ stdcall GetFileType(long) kernel32.GetFileType -@ stub GetFinalPathNameByHandleA -@ stub GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) kernel32.GetFinalPathNameByHandleA +@ stdcall GetFinalPathNameByHandleW(long ptr long long) kernel32.GetFinalPathNameByHandleW @ stdcall GetFullPathNameA(str long ptr ptr) kernel32.GetFullPathNameA @ stdcall GetFullPathNameW(wstr long ptr ptr) kernel32.GetFullPathNameW @ stdcall GetLogicalDriveStringsW(long ptr) kernel32.GetLogicalDriveStringsW diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index e262e2f7898..bd9e6891fcb 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -2912,3 +2912,214 @@ DWORD WINAPI K32GetDeviceDriverFileNameW(void *image_base, LPWSTR file_name, DWO return 0; } + +/*********************************************************************** + * GetFinalPathNameByHandleW (KERNEL32.@) + */ +DWORD WINAPI GetFinalPathNameByHandleW(HANDLE file, LPWSTR path, DWORD charcount, DWORD flags) +{ + WCHAR buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH + 1]; + OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)&buffer; + WCHAR drive_part[MAX_PATH]; + DWORD drive_part_len = 0; + NTSTATUS status; + DWORD result = 0; + ULONG dummy; + WCHAR *ptr; + + TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags ); + + if (flags & ~(FILE_NAME_OPENED | VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT)) + { + WARN("Unknown flags: %x\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + /* get object name */ + status = NtQueryObject( file, ObjectNameInformation, &buffer, sizeof(buffer) - sizeof(WCHAR), &dummy ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError( status ) ); + return 0; + } + if (!info->Name.Buffer) + { + SetLastError( ERROR_INVALID_HANDLE ); + return 0; + } + if (info->Name.Length < 4 * sizeof(WCHAR) || info->Name.Buffer[0] != '\\' || + info->Name.Buffer[1] != '?' || info->Name.Buffer[2] != '?' || info->Name.Buffer[3] != '\\' ) + { + FIXME("Unexpected object name: %s\n", debugstr_wn(info->Name.Buffer, info->Name.Length / sizeof(WCHAR))); + SetLastError( ERROR_GEN_FAILURE ); + return 0; + } + + /* add terminating null character, remove "\\??\\" */ + info->Name.Buffer[info->Name.Length / sizeof(WCHAR)] = 0; + info->Name.Length -= 4 * sizeof(WCHAR); + info->Name.Buffer += 4; + + /* FILE_NAME_OPENED is not supported yet, and would require Wineserver changes */ + if (flags & FILE_NAME_OPENED) + { + FIXME("FILE_NAME_OPENED not supported\n"); + flags &= ~FILE_NAME_OPENED; + } + + /* 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; + + drive_part_len = strlenW(drive_part); + if (!drive_part_len || drive_part_len > strlenW(info->Name.Buffer) || + drive_part[drive_part_len-1] != '\\' || + strncmpiW( info->Name.Buffer, drive_part, drive_part_len )) + { + 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 ); + return 0; + } + } + + if (flags == VOLUME_NAME_NONE) + { + ptr = info->Name.Buffer + drive_part_len - 1; + result = strlenW(ptr); + if (result < charcount) + memcpy(path, ptr, (result + 1) * sizeof(WCHAR)); + else result++; + } + else if (flags == VOLUME_NAME_GUID) + { + WCHAR volume_prefix[51]; + + /* GetVolumeNameForVolumeMountPointW sets error code on failure */ + if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) + return 0; + + ptr = info->Name.Buffer + drive_part_len; + result = strlenW(volume_prefix) + strlenW(ptr); + if (result < charcount) + { + path[0] = 0; + strcatW(path, volume_prefix); + strcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_NT) + { + WCHAR nt_prefix[MAX_PATH]; + + /* QueryDosDeviceW sets error code on failure */ + drive_part[drive_part_len - 1] = 0; + if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) + return 0; + + ptr = info->Name.Buffer + drive_part_len - 1; + result = strlenW(nt_prefix) + strlenW(ptr); + if (result < charcount) + { + path[0] = 0; + strcatW(path, nt_prefix); + strcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_DOS) + { + static const WCHAR dos_prefix[] = {'\\','\\','?','\\', '\0'}; + + result = strlenW(dos_prefix) + strlenW(info->Name.Buffer); + if (result < charcount) + { + path[0] = 0; + strcatW(path, dos_prefix); + strcatW(path, info->Name.Buffer); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else + { + /* Windows crashes here, but we prefer returning ERROR_INVALID_PARAMETER */ + WARN("Invalid combination of flags: %x\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + } + + return result; +} + +/*********************************************************************** + * GetFinalPathNameByHandleA (KERNEL32.@) + */ +DWORD WINAPI GetFinalPathNameByHandleA(HANDLE file, LPSTR path, DWORD charcount, DWORD flags) +{ + WCHAR *str; + DWORD result, len, cp; + + TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags); + + len = GetFinalPathNameByHandleW(file, NULL, 0, flags); + if (len == 0) + return 0; + + str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!str) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + result = GetFinalPathNameByHandleW(file, str, len, flags); + if (result != len - 1) + { + HeapFree(GetProcessHeap(), 0, str); + WARN("GetFinalPathNameByHandleW failed unexpectedly: %u\n", result); + return 0; + } + + cp = oem_file_apis ? CP_OEMCP : CP_ACP; + + len = WideCharToMultiByte(cp, 0, str, -1, NULL, 0, NULL, NULL); + if (!len) + { + HeapFree(GetProcessHeap(), 0, str); + WARN("Failed to get multibyte length\n"); + return 0; + } + + if (charcount < len) + { + HeapFree(GetProcessHeap(), 0, str); + return len - 1; + } + + len = WideCharToMultiByte(cp, 0, str, -1, path, charcount, NULL, NULL); + if (!len) + { + HeapFree(GetProcessHeap(), 0, str); + WARN("WideCharToMultiByte failed\n"); + return 0; + } + + HeapFree(GetProcessHeap(), 0, str); + + return len - 1; +} diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 3452360296f..73f2e882a47 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -682,8 +682,8 @@ @ stdcall GetFileSizeEx(long ptr) @ stdcall GetFileTime(long ptr ptr ptr) @ stdcall GetFileType(long) -# @ stub GetFinalPathNameByHandleA -# @ stub GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) +@ stdcall GetFinalPathNameByHandleW(long ptr long long) @ stdcall GetFirmwareEnvironmentVariableA(str str ptr long) @ stdcall GetFirmwareEnvironmentVariableW(wstr wstr ptr long) @ stdcall GetFullPathNameA(str long ptr ptr) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index a066692087a..ab94c012b0e 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -4453,10 +4453,29 @@ static void test_GetFinalPathNameByHandleA(void) strcpy(dos_path, dos_prefix); strcat(dos_path, long_path); + count = pGetFinalPathNameByHandleA(INVALID_HANDLE_VALUE, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %u\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + file = CreateFileA(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); ok(file != INVALID_HANDLE_VALUE, "CreateFileA error %u\n", GetLastError()); + if (0) { + /* Windows crashes on NULL path */ + count = pGetFinalPathNameByHandleA(file, NULL, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %u\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + } + + /* Test 0-length path */ + count = pGetFinalPathNameByHandleA(file, result_path, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", lstrlenA(dos_path), count); + + /* Test 0 and NULL path */ + count = pGetFinalPathNameByHandleA(file, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", lstrlenA(dos_path), count); + /* Test VOLUME_NAME_DOS with sufficient buffer size */ memset(result_path, 0x11, sizeof(result_path)); count = pGetFinalPathNameByHandleA(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); @@ -4518,6 +4537,10 @@ static void test_GetFinalPathNameByHandleW(void) ok(count == 0, "Expected length 0, got %u\n", count); ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + count = pGetFinalPathNameByHandleW(INVALID_HANDLE_VALUE, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %u\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + count = GetTempPathW(MAX_PATH, temp_path); ok(count, "Failed to get temp path, error %u\n", GetLastError()); ret = GetTempFileNameW(temp_path, prefix, 0, test_path); @@ -4531,6 +4554,23 @@ static void test_GetFinalPathNameByHandleW(void) CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %u\n", GetLastError()); + if (0) { + /* Windows crashes on NULL path */ + count = pGetFinalPathNameByHandleW(file, NULL, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %u\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError()); + } + + /* Test 0-length path */ + count = pGetFinalPathNameByHandleW(file, result_path, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path) + 1 || + broken(count == lstrlenW(dos_path) + 2), "Expected length %u, got %u\n", lstrlenW(dos_path) + 1, count); + + /* Test 0 and NULL path */ + count = pGetFinalPathNameByHandleW(file, NULL, 0, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path) + 1 || + broken(count == lstrlenW(dos_path) + 2), "Expected length %u, got %u\n", lstrlenW(dos_path) + 1, count); + /* Test VOLUME_NAME_DOS with sufficient buffer size */ memset(result_path, 0x11, sizeof(result_path)); count = pGetFinalPathNameByHandleW(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 50c1ae20c91..53ed586ef60 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -235,8 +235,8 @@ @ stdcall GetFileSizeEx(long ptr) kernel32.GetFileSizeEx @ stdcall GetFileTime(long ptr ptr ptr) kernel32.GetFileTime @ stdcall GetFileType(long) kernel32.GetFileType -@ stub GetFinalPathNameByHandleA -@ stub GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) kernel32.GetFinalPathNameByHandleA +@ stdcall GetFinalPathNameByHandleW(long ptr long long) kernel32.GetFinalPathNameByHandleW @ stdcall GetFullPathNameA(str long ptr ptr) kernel32.GetFullPathNameA @ stdcall GetFullPathNameW(wstr long ptr ptr) kernel32.GetFullPathNameW @ stdcall GetHandleInformation(long ptr) kernel32.GetHandleInformation