From ea5ae1b1a3c8654b83f7bcdccfd29fc644e7560e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Tue, 12 Aug 2014 20:24:14 +0200
Subject: kernel32: Implement GetFinalPathNameByHandle.

---
 .../api-ms-win-core-file-l1-2-0.spec               |   4 +-
 dlls/kernel32/file.c                               | 182 +++++++++++++++++++++
 dlls/kernel32/kernel32.spec                        |   4 +-
 include/fileapi.h                                  |   8 +
 4 files changed, 194 insertions(+), 4 deletions(-)

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 ebfd52e..cddf112 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/kernel32/file.c b/dlls/kernel32/file.c
index 006db1c..b3cc8c3 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -2829,3 +2829,185 @@ 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;
+    NTSTATUS status;
+    DWORD result = 0;
+    ULONG dummy;
+    WCHAR *ptr;
+
+    TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags );
+
+    /* check for invalid arguments */
+    if (!path)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return 0;
+    }
+    else if (file == INVALID_HANDLE_VALUE)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return 0;
+    }
+    else if (flags & ~(FILE_NAME_OPENED | VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT))
+    {
+        WARN("Invalid or unsupported 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;
+    }
+    else 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 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 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 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;
+
+    TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags );
+
+    if (!path || !charcount)
+        return GetFinalPathNameByHandleW(file, (LPWSTR)path, charcount, flags);
+
+    str = HeapAlloc( GetProcessHeap(), 0, charcount * sizeof(WCHAR) );
+    if (!str)
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return 0;
+    }
+
+    result = GetFinalPathNameByHandleW(file, (LPWSTR)str, charcount, flags);
+    if (result)
+    {
+        if (result < charcount)
+        {
+            result = FILE_name_WtoA( str, result, path, charcount - 1 );
+            path[result] = 0;
+        }
+        else result--; /* Why does Windows do this? */
+    }
+
+    HeapFree( GetProcessHeap(), 0, str );
+    return result;
+}
\ No newline at end of file
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 3719505..a272535 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/include/fileapi.h b/include/fileapi.h
index 02bbbd4..0ccf9e9 100644
--- a/include/fileapi.h
+++ b/include/fileapi.h
@@ -34,6 +34,14 @@ typedef struct _CREATEFILE2_EXTENDED_PARAMETERS {
 
 WINBASEAPI HANDLE WINAPI CreateFile2(LPCWSTR,DWORD,DWORD,DWORD,LPCREATEFILE2_EXTENDED_PARAMETERS);
 
+#define FILE_NAME_NORMALIZED    0x0
+#define FILE_NAME_OPENED        0x8
+
+#define VOLUME_NAME_DOS         0x0
+#define VOLUME_NAME_GUID        0x1
+#define VOLUME_NAME_NT          0x2
+#define VOLUME_NAME_NONE        0x4
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.2.1