From f0eee9d4ed2009f24799a149a70e0848dda9fe14 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Wed, 13 Mar 2019 16:02:05 -0600
Subject: kernel32: Implement CreateSymbolicLink[A|W] with ntdll reparse
 points.

Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
 dlls/kernel32/tests/path.c     |  94 ++++++++++++++++++++++++++++++
 dlls/kernelbase/file.c         | 103 ++++++++++++++++++++++++++++++++-
 dlls/msvcp120/tests/msvcp120.c |  75 +++++++++++-------------
 dlls/msvcp140/tests/msvcp140.c |  63 +++++++++-----------
 4 files changed, 256 insertions(+), 79 deletions(-)

diff --git a/dlls/kernel32/tests/path.c b/dlls/kernel32/tests/path.c
index 0e45ad44ff3..0f8d94e2957 100644
--- a/dlls/kernel32/tests/path.c
+++ b/dlls/kernel32/tests/path.c
@@ -83,6 +83,9 @@ static NTSTATUS (WINAPI *pLdrGetDllPath)(LPCWSTR,ULONG,LPWSTR*,LPWSTR*);
 static BOOL (WINAPI *pCheckNameLegalDOS8Dot3W)(const WCHAR *, char *, DWORD, BOOL *, BOOL *);
 static BOOL (WINAPI *pCheckNameLegalDOS8Dot3A)(const char *, char *, DWORD, BOOL *, BOOL *);
 
+/* Present in Vista+ */
+static BOOL (WINAPI *pCreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD);
+
 /* a structure to deal with wine todos somewhat cleanly */
 typedef struct {
   DWORD shortlen;
@@ -2147,6 +2150,7 @@ static void init_pointers(void)
     MAKEFUNC(SetDefaultDllDirectories);
     MAKEFUNC(CheckNameLegalDOS8Dot3W);
     MAKEFUNC(CheckNameLegalDOS8Dot3A);
+    MAKEFUNC(CreateSymbolicLinkW);
     mod = GetModuleHandleA("ntdll.dll");
     MAKEFUNC(LdrGetDllPath);
     MAKEFUNC(RtlGetExePath);
@@ -2690,6 +2694,95 @@ static void test_LdrGetDllPath(void)
     SetEnvironmentVariableW( pathW, old_path );
 }
 
+static void test_CreateSymbolicLink(void)
+{
+    static const WCHAR target_fileW[] = {'t','a','r','g','e','t','_','f','i','l','e',0};
+    static const WCHAR target_dirW[] = {'t','a','r','g','e','t','_','d','i','r',0};
+    static const WCHAR linkW[] = {'l','i','n','k',0};
+    static const WCHAR fooW[] = {'f','o','o',0};
+    static WCHAR volW[] = {'c',':','\\',0};
+    static const WCHAR dotW[] = {'.',0};
+    WCHAR path[MAX_PATH], old_path[MAX_PATH], tmp[MAX_PATH];
+    DWORD dwLen, dwFlags;
+    TOKEN_PRIVILEGES tp;
+    HANDLE token;
+    LUID luid;
+    BOOL bret;
+    HANDLE h;
+
+    if (!pCreateSymbolicLinkW)
+    {
+        win_skip( "CreateSymbolicLink isn't available\n" );
+        return;
+    }
+
+    /* Create a temporary folder for the symlink tests */
+    GetTempFileNameW( dotW, fooW, 0, path );
+    DeleteFileW( path );
+    if (!CreateDirectoryW( path, NULL ))
+    {
+        win_skip("Unable to create a temporary reparse point directory.\n");
+        return;
+    }
+    GetCurrentDirectoryW( sizeof(old_path)/sizeof(WCHAR), old_path );
+    SetCurrentDirectoryW( path );
+
+    /* Check that the volume this folder is located on supports reparse points */
+    GetFullPathNameW( path, sizeof(tmp)/sizeof(WCHAR), tmp, NULL );
+    volW[0] = tmp[0];
+    GetVolumeInformationW( volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0 );
+    if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
+    {
+        skip("File system does not support reparse points.\n");
+        goto cleanup;
+    }
+
+    /* Establish permissions for symlink creation */
+    bret = OpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &token );
+    ok(bret, "OpenProcessToken failed: %u\n", GetLastError());
+    bret = LookupPrivilegeValueA( NULL, "SeCreateSymbolicLinkPrivilege", &luid );
+    todo_wine ok(bret || broken(!bret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE) /* winxp */,
+                 "LookupPrivilegeValue failed: %u\n", GetLastError());
+    if (bret)
+    {
+        tp.PrivilegeCount = 1;
+        tp.Privileges[0].Luid = luid;
+        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+        bret = AdjustTokenPrivileges( token, FALSE, &tp, 0, NULL, NULL );
+        ok(bret, "AdjustTokenPrivileges failed: %u\n", GetLastError());
+    }
+    if ((!bret && GetLastError() != ERROR_NO_SUCH_PRIVILEGE) || GetLastError() == ERROR_NOT_ALL_ASSIGNED)
+    {
+        win_skip("Insufficient permissions to perform symlink tests.\n");
+        goto cleanup;
+    }
+
+    /* Create a destination folder and file for symlinks to target */
+    bret = CreateDirectoryW( target_dirW, NULL );
+    ok(bret, "Failed to create symlink target directory.\n");
+    h = CreateFileW( target_fileW, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok(h != INVALID_HANDLE_VALUE, "Failed to create symlink target file.\n");
+    CloseHandle( h );
+
+    /* Create a directory symbolic link */
+    bret = CreateSymbolicLinkW( linkW, target_dirW, SYMBOLIC_LINK_FLAG_DIRECTORY );
+    ok(bret, "Failed to create directory symbolic link! (0x%x)\n", GetLastError());
+    bret = RemoveDirectoryW( linkW );
+    ok(bret, "Failed to remove directory symbolic link! (0x%x)\n", GetLastError());
+
+    /* Create a file symbolic link */
+    bret = CreateSymbolicLinkW( linkW, target_fileW, 0x0 );
+    ok(bret, "Failed to create file symbolic link! (0x%x)\n", GetLastError());
+    bret = DeleteFileW( linkW );
+    ok(bret, "Failed to remove file symbolic link! (0x%x)\n", GetLastError());
+
+cleanup:
+    DeleteFileW( target_fileW );
+    RemoveDirectoryW( target_dirW );
+    SetCurrentDirectoryW( old_path );
+    RemoveDirectoryW( path );
+}
+
 START_TEST(path)
 {
     CHAR origdir[MAX_PATH],curdir[MAX_PATH], curDrive, otherDrive;
@@ -2719,4 +2812,5 @@ START_TEST(path)
     test_RtlGetSearchPath();
     test_RtlGetExePath();
     test_LdrGetDllPath();
+    test_CreateSymbolicLink();
 }
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index d9b34408692..2e67f6eaa3e 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -38,6 +38,7 @@
 #include "shlwapi.h"
 #include "ddk/ntddk.h"
 #include "ddk/ntddser.h"
+#include "ntifs.h"
 
 #include "kernelbase.h"
 #include "wine/exception.h"
@@ -926,8 +927,106 @@ done:
  */
 BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags )
 {
-    FIXME( "(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags );
-    return TRUE;
+    static INT struct_size = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]);
+    static INT header_size = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer);
+    INT buffer_size, data_size, string_len, prefix_len;
+    WCHAR *subst_dest, *print_dest, *string;
+    REPARSE_DATA_BUFFER *buffer;
+    LPWSTR target_path = NULL;
+    BOOL is_relative, is_dir;
+    int target_path_len = 0;
+    UNICODE_STRING nt_name;
+    BOOLEAN bret = FALSE;
+    NTSTATUS status;
+    HANDLE hlink;
+    DWORD dwret;
+
+    TRACE( "(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags );
+
+    is_relative = (RtlDetermineDosPathNameType_U( target ) == RELATIVE_PATH);
+    is_dir = (flags & SYMBOLIC_LINK_FLAG_DIRECTORY);
+    if (is_dir && !CreateDirectoryW( link, NULL ))
+        return FALSE;
+    hlink = CreateFileW( link, GENERIC_READ | GENERIC_WRITE, 0, 0,
+                         is_dir ? OPEN_EXISTING : CREATE_NEW,
+                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0 );
+    if (hlink == INVALID_HANDLE_VALUE)
+        goto cleanup;
+    if (is_relative)
+    {
+        UNICODE_STRING nt_path;
+        int len;
+
+        status = RtlDosPathNameToNtPathName_U_WithStatus( link, &nt_path, NULL, NULL );
+        if (status != STATUS_SUCCESS)
+        {
+            SetLastError( RtlNtStatusToDosError(status) );
+            goto cleanup;
+        }
+        /* obtain the path of the link */
+        for (; nt_path.Length > 0; nt_path.Length -= sizeof(WCHAR))
+        {
+            WCHAR c = nt_path.Buffer[nt_path.Length/sizeof(WCHAR)];
+            if (c == '/' || c == '\\')
+            {
+                nt_path.Length += sizeof(WCHAR);
+                break;
+            }
+        }
+        /* append the target to the link path */
+        target_path_len = nt_path.Length / sizeof(WCHAR);
+        len = target_path_len + (lstrlenW( target ) + 1);
+        target_path = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(WCHAR) );
+        lstrcpynW( target_path, nt_path.Buffer, target_path_len+1 );
+        target_path[target_path_len+1] = 0;
+        lstrcatW( target_path, target );
+        RtlFreeUnicodeString( &nt_path );
+    }
+    else
+        target_path = (LPWSTR)target;
+    status = RtlDosPathNameToNtPathName_U_WithStatus( target_path, &nt_name, NULL, NULL );
+    if (status != STATUS_SUCCESS)
+    {
+        SetLastError( RtlNtStatusToDosError(status) );
+        goto cleanup;
+    }
+    if (is_relative && _wcsnicmp( target_path, nt_name.Buffer, target_path_len ) != 0)
+    {
+        SetLastError( RtlNtStatusToDosError(status) );
+        goto cleanup;
+    }
+    prefix_len = is_relative ? 0 : strlen("\\??\\");
+    string = &nt_name.Buffer[target_path_len];
+    string_len = lstrlenW( &string[prefix_len] );
+    data_size = (prefix_len + 2 * string_len + 2) * sizeof(WCHAR);
+    buffer_size = struct_size + data_size;
+    buffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size );
+    buffer->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+    buffer->ReparseDataLength = struct_size - header_size + data_size;
+    buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
+    buffer->SymbolicLinkReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
+    buffer->SymbolicLinkReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
+    buffer->SymbolicLinkReparseBuffer.Flags = is_relative ? SYMLINK_FLAG_RELATIVE : 0;
+    subst_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[0];
+    print_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[prefix_len + string_len + 1];
+    lstrcpyW( subst_dest, string );
+    lstrcpyW( print_dest, &string[prefix_len] );
+    RtlFreeUnicodeString( &nt_name );
+    bret = DeviceIoControl( hlink, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_size, NULL, 0,
+                            &dwret, 0 );
+    HeapFree( GetProcessHeap(), 0, buffer );
+
+cleanup:
+    CloseHandle( hlink );
+    if (!bret)
+    {
+        if (is_dir)
+            RemoveDirectoryW( link );
+        else
+            DeleteFileW( link );
+    }
+    if (is_relative) HeapFree( GetProcessHeap(), 0, target_path );
+    return bret;
 }
 
 
diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c
index ed632a27df2..bd2e8550d05 100644
--- a/dlls/msvcp120/tests/msvcp120.c
+++ b/dlls/msvcp120/tests/msvcp120.c
@@ -1615,15 +1615,14 @@ static void test_tr2_sys__Stat(void)
         char const *path;
         enum file_type ret;
         int err_code;
-        int is_todo;
     } tests[] = {
-        { NULL, status_unknown, ERROR_INVALID_PARAMETER, FALSE },
-        { "tr2_test_dir",    directory_file, ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\f1",  regular_file, ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\not_exist_file  ", file_not_found, ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\??invalid_name>>", file_not_found, ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\f1_link" ,   regular_file, ERROR_SUCCESS, TRUE },
-        { "tr2_test_dir\\dir_link", directory_file, ERROR_SUCCESS, TRUE },
+        { NULL, status_unknown, ERROR_INVALID_PARAMETER },
+        { "tr2_test_dir",    directory_file, ERROR_SUCCESS },
+        { "tr2_test_dir\\f1",  regular_file, ERROR_SUCCESS },
+        { "tr2_test_dir\\not_exist_file  ", file_not_found, ERROR_SUCCESS },
+        { "tr2_test_dir\\??invalid_name>>", file_not_found, ERROR_SUCCESS },
+        { "tr2_test_dir\\f1_link" ,   regular_file, ERROR_SUCCESS },
+        { "tr2_test_dir\\dir_link", directory_file, ERROR_SUCCESS },
     };
     WCHAR testW[] = {'t','r','2','_','t','e','s','t','_','d','i','r',0};
     WCHAR testW2[] = {'t','r','2','_','t','e','s','t','_','d','i','r','/','f','1',0};
@@ -1668,16 +1667,14 @@ static void test_tr2_sys__Stat(void)
     for(i=0; i<ARRAY_SIZE(tests); i++) {
         err_code = 0xdeadbeef;
         val = p_tr2_sys__Stat(tests[i].path, &err_code);
-        todo_wine_if(tests[i].is_todo)
-            ok(tests[i].ret == val, "tr2_sys__Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].ret == val, "tr2_sys__Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
         ok(tests[i].err_code == err_code, "tr2_sys__Stat(): test %d err_code expect: %d, got %d\n",
                 i+1, tests[i].err_code, err_code);
 
         /* test tr2_sys__Lstat */
         err_code = 0xdeadbeef;
         val = p_tr2_sys__Lstat(tests[i].path, &err_code);
-        todo_wine_if(tests[i].is_todo)
-            ok(tests[i].ret == val, "tr2_sys__Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].ret == val, "tr2_sys__Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
         ok(tests[i].err_code == err_code, "tr2_sys__Lstat(): test %d err_code expect: %d, got %d\n",
                 i+1, tests[i].err_code, err_code);
     }
@@ -1692,8 +1689,8 @@ static void test_tr2_sys__Stat(void)
     ok(ERROR_SUCCESS == err_code, "tr2_sys__Lstat_wchar(): err_code expect ERROR_SUCCESS, got %d\n", err_code);
 
     if(ret) {
-        todo_wine ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n");
-        todo_wine ok(RemoveDirectoryA("tr2_test_dir/dir_link"), "expect tr2_test_dir/dir_link to exist\n");
+        ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n");
+        ok(RemoveDirectoryA("tr2_test_dir/dir_link"), "expect tr2_test_dir/dir_link to exist\n");
     }
     ok(DeleteFileA("tr2_test_dir/f1"), "expect tr2_test_dir/f1 to exist\n");
     ok(RemoveDirectoryA("tr2_test_dir"), "expect tr2_test_dir to exist\n");
@@ -1923,16 +1920,15 @@ static void test_tr2_sys__Symlink(void)
         char const *existing_path;
         char const *new_path;
         int last_error;
-        MSVCP_bool is_todo;
     } tests[] = {
-        { "f1", "f1_link", ERROR_SUCCESS, FALSE },
-        { "f1", "tr2_test_dir\\f1_link", ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\f1_link", "tr2_test_dir\\f1_link_link", ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir", "dir_link", ERROR_SUCCESS, FALSE },
-        { NULL, "NULL_link", ERROR_INVALID_PARAMETER, FALSE },
-        { "f1", NULL, ERROR_INVALID_PARAMETER, FALSE },
-        { "not_exist",  "not_exist_link", ERROR_SUCCESS, FALSE },
-        { "f1", "not_exist_dir\\f1_link", ERROR_PATH_NOT_FOUND, TRUE }
+        { "f1", "f1_link", ERROR_SUCCESS },
+        { "f1", "tr2_test_dir\\f1_link", ERROR_SUCCESS },
+        { "tr2_test_dir\\f1_link", "tr2_test_dir\\f1_link_link", ERROR_SUCCESS },
+        { "tr2_test_dir", "dir_link", ERROR_SUCCESS },
+        { NULL, "NULL_link", ERROR_INVALID_PARAMETER },
+        { "f1", NULL, ERROR_INVALID_PARAMETER },
+        { "not_exist",  "not_exist_link", ERROR_SUCCESS },
+        { "f1", "not_exist_dir\\f1_link", ERROR_PATH_NOT_FOUND }
     };
 
     ret = p_tr2_sys__Make_dir("tr2_test_dir");
@@ -1957,18 +1953,17 @@ static void test_tr2_sys__Symlink(void)
         }
 
         ok(errno == 0xdeadbeef, "tr2_sys__Symlink(): test %d errno expect 0xdeadbeef, got %d\n", i+1, errno);
-        todo_wine_if(tests[i].is_todo)
-            ok(ret == tests[i].last_error, "tr2_sys__Symlink(): test %d expect: %d, got %d\n", i+1, tests[i].last_error, ret);
+        ok(ret == tests[i].last_error, "tr2_sys__Symlink(): test %d expect: %d, got %d\n", i+1, tests[i].last_error, ret);
         if(ret == ERROR_SUCCESS)
             ok(p_tr2_sys__File_size(tests[i].new_path) == 0, "tr2_sys__Symlink(): expect 0, got %s\n", wine_dbgstr_longlong(p_tr2_sys__File_size(tests[i].new_path)));
     }
 
     ok(DeleteFileA("f1"), "expect f1 to exist\n");
-    todo_wine ok(DeleteFileA("f1_link"), "expect f1_link to exist\n");
-    todo_wine ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n");
-    todo_wine ok(DeleteFileA("tr2_test_dir/f1_link_link"), "expect tr2_test_dir/f1_link_link to exist\n");
-    todo_wine ok(DeleteFileA("not_exist_link"), "expect not_exist_link to exist\n");
-    todo_wine ok(DeleteFileA("dir_link"), "expect dir_link to exist\n");
+    ok(DeleteFileA("f1_link"), "expect f1_link to exist\n");
+    ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n");
+    ok(DeleteFileA("tr2_test_dir/f1_link_link"), "expect tr2_test_dir/f1_link_link to exist\n");
+    ok(DeleteFileA("not_exist_link"), "expect not_exist_link to exist\n");
+    ok(DeleteFileA("dir_link"), "expect dir_link to exist\n");
     ret = p_tr2_sys__Remove_dir("tr2_test_dir");
     ok(ret == 1, "tr2_sys__Remove_dir(): expect 1 got %d\n", ret);
 }
@@ -1982,15 +1977,14 @@ static void test_tr2_sys__Unlink(void)
     struct {
         char const *path;
         int last_error;
-        MSVCP_bool is_todo;
     } tests[] = {
-        { "tr2_test_dir\\f1_symlink", ERROR_SUCCESS, TRUE },
-        { "tr2_test_dir\\f1_link", ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir\\f1", ERROR_SUCCESS, FALSE },
-        { "tr2_test_dir", ERROR_ACCESS_DENIED, FALSE },
-        { "not_exist", ERROR_FILE_NOT_FOUND, FALSE },
-        { "not_exist_dir\\not_exist_file", ERROR_PATH_NOT_FOUND, FALSE },
-        { NULL, ERROR_PATH_NOT_FOUND, FALSE }
+        { "tr2_test_dir\\f1_symlink", ERROR_SUCCESS },
+        { "tr2_test_dir\\f1_link", ERROR_SUCCESS },
+        { "tr2_test_dir\\f1", ERROR_SUCCESS },
+        { "tr2_test_dir", ERROR_ACCESS_DENIED },
+        { "not_exist", ERROR_FILE_NOT_FOUND },
+        { "not_exist_dir\\not_exist_file", ERROR_PATH_NOT_FOUND },
+        { NULL, ERROR_PATH_NOT_FOUND }
     };
 
     GetCurrentDirectoryA(MAX_PATH, current_path);
@@ -2019,9 +2013,8 @@ static void test_tr2_sys__Unlink(void)
     for(i=0; i<ARRAY_SIZE(tests); i++) {
         errno = 0xdeadbeef;
         ret = p_tr2_sys__Unlink(tests[i].path);
-        todo_wine_if(tests[i].is_todo)
-            ok(ret == tests[i].last_error, "tr2_sys__Unlink(): test %d expect: %d, got %d\n",
-                    i+1, tests[i].last_error, ret);
+        ok(ret == tests[i].last_error, "tr2_sys__Unlink(): test %d expect: %d, got %d\n",
+           i+1, tests[i].last_error, ret);
         ok(errno == 0xdeadbeef, "tr2_sys__Unlink(): test %d errno expect: 0xdeadbeef, got %d\n", i+1, ret);
     }
 
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c
index 751b1beed86..382f5732c29 100644
--- a/dlls/msvcp140/tests/msvcp140.c
+++ b/dlls/msvcp140/tests/msvcp140.c
@@ -803,16 +803,15 @@ static void test_Stat(void)
         WCHAR const *path;
         enum file_type ret;
         int perms;
-        int is_todo;
     } tests[] = {
-        { NULL, file_not_found, 0xdeadbeef, FALSE },
-        { test_dirW, directory_file, 0777, FALSE },
-        { test_f1W, regular_file, 0777, FALSE },
-        { test_f2W, regular_file, 0555, FALSE },
-        { test_neW, file_not_found, 0xdeadbeef, FALSE },
-        { test_invW, file_not_found, 0xdeadbeef, FALSE },
-        { test_f1_linkW, regular_file, 0777, TRUE },
-        { test_dir_linkW, directory_file, 0777, TRUE },
+        { NULL, file_not_found, 0xdeadbeef },
+        { test_dirW, directory_file, 0777 },
+        { test_f1W, regular_file, 0777 },
+        { test_f2W, regular_file, 0555 },
+        { test_neW, file_not_found, 0xdeadbeef },
+        { test_invW, file_not_found, 0xdeadbeef },
+        { test_f1_linkW, regular_file, 0777 },
+        { test_dir_linkW, directory_file, 0777 },
     };
 
     GetCurrentDirectoryW(MAX_PATH, origin_path);
@@ -867,26 +866,20 @@ static void test_Stat(void)
     for(i=0; i<ARRAY_SIZE(tests); i++) {
         perms = 0xdeadbeef;
         val = p_Stat(tests[i].path, &perms);
-        todo_wine_if(tests[i].is_todo) {
-            ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
-            ok(tests[i].perms == perms, "_Stat(): test %d perms expect: 0%o, got 0%o\n",
-                    i+1, tests[i].perms, perms);
-        }
+        ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].perms == perms, "_Stat(): test %d perms expect: 0%o, got 0%o\n",
+                i+1, tests[i].perms, perms);
         val = p_Stat(tests[i].path, NULL);
-        todo_wine_if(tests[i].is_todo)
-            ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].ret == val, "_Stat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
 
         /* test _Lstat */
         perms = 0xdeadbeef;
         val = p_Lstat(tests[i].path, &perms);
-        todo_wine_if(tests[i].is_todo) {
-            ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
-            ok(tests[i].perms == perms, "_Lstat(): test %d perms expect: 0%o, got 0%o\n",
-                    i+1, tests[i].perms, perms);
-        }
+        ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].perms == perms, "_Lstat(): test %d perms expect: 0%o, got 0%o\n",
+                i+1, tests[i].perms, perms);
         val = p_Lstat(tests[i].path, NULL);
-        todo_wine_if(tests[i].is_todo)
-            ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
+        ok(tests[i].ret == val, "_Lstat(): test %d expect: %d, got %d\n", i+1, tests[i].ret, val);
     }
 
     GetSystemDirectoryW(sys_path, MAX_PATH);
@@ -898,8 +891,8 @@ static void test_Stat(void)
     ok(perms == expected_perms, "_Stat(): perms expect: 0%o, got 0%o\n", expected_perms, perms);
 
     if(ret) {
-        todo_wine ok(DeleteFileW(test_f1_linkW), "expect wine_test_dir/f1_link to exist\n");
-        todo_wine ok(RemoveDirectoryW(test_dir_linkW), "expect wine_test_dir/dir_link to exist\n");
+        ok(DeleteFileW(test_f1_linkW), "expect wine_test_dir/f1_link to exist\n");
+        ok(RemoveDirectoryW(test_dir_linkW), "expect wine_test_dir/dir_link to exist\n");
     }
     ok(DeleteFileW(test_f1W), "expect wine_test_dir/f1 to exist\n");
     SetFileAttributesW(test_f2W, FILE_ATTRIBUTE_NORMAL);
@@ -1044,15 +1037,14 @@ static void test_Unlink(void)
     struct {
         WCHAR const *path;
         int last_error;
-        MSVCP_bool is_todo;
     } tests[] = {
-        { f1_symlinkW, ERROR_SUCCESS, TRUE },
-        { f1_linkW, ERROR_SUCCESS, FALSE },
-        { f1W, ERROR_SUCCESS, FALSE },
-        { wine_test_dirW, ERROR_ACCESS_DENIED, FALSE },
-        { not_existW, ERROR_FILE_NOT_FOUND, FALSE },
-        { not_exist_fileW, ERROR_PATH_NOT_FOUND, FALSE },
-        { NULL, ERROR_PATH_NOT_FOUND, FALSE }
+        { f1_symlinkW, ERROR_SUCCESS },
+        { f1_linkW, ERROR_SUCCESS },
+        { f1W, ERROR_SUCCESS },
+        { wine_test_dirW, ERROR_ACCESS_DENIED },
+        { not_existW, ERROR_FILE_NOT_FOUND },
+        { not_exist_fileW, ERROR_PATH_NOT_FOUND },
+        { NULL, ERROR_PATH_NOT_FOUND }
     };
 
     GetCurrentDirectoryW(MAX_PATH, current_path);
@@ -1081,9 +1073,8 @@ static void test_Unlink(void)
     for(i=0; i<ARRAY_SIZE(tests); i++) {
         errno = 0xdeadbeef;
         ret = p_Unlink(tests[i].path);
-        todo_wine_if(tests[i].is_todo)
-            ok(ret == tests[i].last_error, "_Unlink(): test %d expect: %d, got %d\n",
-                    i+1, tests[i].last_error, ret);
+        ok(ret == tests[i].last_error, "_Unlink(): test %d expect: %d, got %d\n",
+           i+1, tests[i].last_error, ret);
         ok(errno == 0xdeadbeef, "_Unlink(): test %d errno expect: 0xdeadbeef, got %d\n", i+1, ret);
     }
 
-- 
2.17.1

