[PATCH] msvcp140: Implement _Stat and _Lstat (v2).

Stefan Dösinger stefan at codeweavers.com
Thu Jul 13 08:05:54 CDT 2017


Differences vs msvcp120 are the different file_type enumeration and
returning mostly useless unix permission info.

Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>

---

Version 2: Re-add the _File_size export lookup that I lost when I sorted
the function pointers alphabetically during the rebase.

The enum names differ from those in the PSDK header because we can't use
the C++ file_type::regular notation and the names are otherwise quite
short and generic. This should be OK because we do not export them in
our headers.

The file_type mismatch between msvcp120 and msvcp140 also affects other
functions like _Open_dir. I'll fix them separately.
---
 dlls/msvcp140/msvcp140.spec    |   4 +-
 dlls/msvcp140/tests/msvcp140.c | 133 +++++++++++++++++++++++++++++++++++++++++
 dlls/msvcp90/ios.c             |  42 +++++++++++++
 3 files changed, 177 insertions(+), 2 deletions(-)

diff --git a/dlls/msvcp140/msvcp140.spec b/dlls/msvcp140/msvcp140.spec
index 22871bca92..afada991d3 100644
--- a/dlls/msvcp140/msvcp140.spec
+++ b/dlls/msvcp140/msvcp140.spec
@@ -3669,7 +3669,7 @@
 @ stub _Last_write_time
 @ stub _Link
 @ cdecl _Lock_shared_ptr_spin_lock()
-@ stub _Lstat
+@ cdecl _Lstat(wstr ptr)
 @ cdecl _Make_dir(wstr) tr2_sys__Make_dir_wchar
 @ cdecl _Mbrtowc(ptr ptr long ptr ptr) _Mbrtowc
 @ stub _Mtx_clear_owner
@@ -3699,7 +3699,7 @@
 @ stub _Set_last_write_time
 @ stub _Sinh
 @ extern _Snan _Snan
-@ stub _Stat
+@ cdecl _Stat(wstr ptr)
 @ stub _Statvfs
 @ cdecl _Stod(ptr ptr long) _Stod
 @ cdecl _Stodx(ptr ptr long ptr) _Stodx
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c
index 7ab1ba5cc6..62afac799a 100644
--- a/dlls/msvcp140/tests/msvcp140.c
+++ b/dlls/msvcp140/tests/msvcp140.c
@@ -135,6 +135,12 @@ typedef struct {
     void *arg;
 } _Threadpool_chore;
 
+enum file_type {
+    file_type_not_found = -1, file_type_none, file_type_regular,
+    file_type_directory, file_type_symlink, file_type_block,
+    file_type_character, file_type_fifo, file_type_socket, file_type_unknown
+};
+
 static unsigned int (__cdecl *p__Thrd_id)(void);
 static task_continuation_context* (__thiscall *p_task_continuation_context_ctor)(task_continuation_context*);
 static void (__thiscall *p__ContextCallback__Assign)(_ContextCallback*, void*);
@@ -155,14 +161,20 @@ static void (__cdecl *p__Release_chore)(_Threadpool_chore*);
 static MSVCP_bool (__cdecl *p_Current_get)(WCHAR *);
 static MSVCP_bool (__cdecl *p_Current_set)(WCHAR const *);
 static ULONGLONG (__cdecl *p_File_size)(WCHAR const *);
+static enum file_type (__cdecl *p_Lstat)(WCHAR const *, int *);
+static enum file_type (__cdecl *p_Stat)(WCHAR const *, int *);
 static int (__cdecl *p_To_byte)(const WCHAR *src, char *dst);
 static int (__cdecl *p_To_wide)(const char *src, WCHAR *dst);
 
+static BOOLEAN (WINAPI *pCreateSymbolicLinkW)(const WCHAR *, const WCHAR *, DWORD);
+
 static HMODULE msvcp;
 #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y)
 #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0)
 static BOOL init(void)
 {
+    HANDLE hdll;
+
     msvcp = LoadLibraryA("msvcp140.dll");
     if(!msvcp)
     {
@@ -222,9 +234,14 @@ static BOOL init(void)
     SET(p_Current_get, "_Current_get");
     SET(p_Current_set, "_Current_set");
     SET(p_File_size, "_File_size");
+    SET(p_Stat, "_Stat");
+    SET(p_Lstat, "_Lstat");
     SET(p_To_byte, "_To_byte");
     SET(p_To_wide, "_To_wide");
 
+    hdll = GetModuleHandleA("kernel32.dll");
+    pCreateSymbolicLinkW = (void*)GetProcAddress(hdll, "CreateSymbolicLinkW");
+
     init_thiscall_thunk();
     return TRUE;
 }
@@ -691,6 +708,121 @@ static void test_Current_set(void)
             wine_dbgstr_w(origin_path), wine_dbgstr_w(current_path));
 }
 
+static void test_Stat(void)
+{
+    int i, perms, ret;
+    HANDLE file;
+    enum file_type val;
+    WCHAR test_dirW[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r',0};
+    WCHAR test_f1W[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','/','f','1',0};
+    WCHAR test_f2W[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','/','f','2',0};
+    WCHAR pipeW[] = {'\\','\\','.','\\','P','i','P','e','\\','t','e','s','t','s','_','p','i','p','e','.','c', 0};
+    WCHAR test_neW[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','/','n','e',0};
+    WCHAR test_invW[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','\\','?','?','i','n','v','a','l','i','d','_','n','a','m','e','>','>',0};
+    WCHAR test_f1_linkW[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','\\','f','1','_','l','i','n','k',0};
+    WCHAR test_dir_linkW[] = {'w','i','n','e','_','t','e','s','t','_','d','i','r','\\','d','i','r','_','l','i','n','k',0};
+    WCHAR sys_path[MAX_PATH], origin_path[MAX_PATH], temp_path[MAX_PATH];
+    struct {
+        WCHAR const *path;
+        enum file_type ret;
+        int perms;
+        int is_todo;
+    } tests[] = {
+        { NULL, file_type_not_found, 0xdeadbeef, FALSE },
+        { test_dirW, file_type_directory, 0777, FALSE },
+        { test_f1W, file_type_regular, 0777, FALSE },
+        { test_f2W, file_type_regular, 0555, FALSE },
+        { test_neW, file_type_not_found, 0xdeadbeef, FALSE },
+        { test_invW, file_type_not_found, 0xdeadbeef, FALSE },
+        { test_f1_linkW, file_type_regular, 0777, TRUE },
+        { test_dir_linkW, file_type_directory, 0777, TRUE },
+    };
+
+    memset(origin_path, 0, sizeof(origin_path));
+    memset(origin_path, 0, sizeof(temp_path));
+    GetCurrentDirectoryW(MAX_PATH, origin_path);
+    GetTempPathW(MAX_PATH, temp_path);
+    ok(SetCurrentDirectoryW(temp_path), "SetCurrentDirectoryW to temp_path failed\n");
+
+    CreateDirectoryW(test_dirW, NULL);
+
+    file = CreateFileW(test_f1W, 0, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "create file failed: INVALID_HANDLE_VALUE\n");
+    ok(CloseHandle(file), "CloseHandle\n");
+
+    file = CreateFileW(test_f2W, 0, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "create file failed: INVALID_HANDLE_VALUE\n");
+    ok(CloseHandle(file), "CloseHandle\n");
+    SetFileAttributesW(test_f2W, FILE_ATTRIBUTE_READONLY);
+
+    SetLastError(0xdeadbeef);
+    ret = pCreateSymbolicLinkW ? pCreateSymbolicLinkW(test_f1_linkW, test_f1W, 0) : FALSE;
+    if(!ret && (!pCreateSymbolicLinkW || GetLastError()==ERROR_PRIVILEGE_NOT_HELD||GetLastError()==ERROR_INVALID_FUNCTION)) {
+        tests[5].ret = tests[6].ret = file_type_not_found;
+        win_skip("Privilege not held or symbolic link not supported, skipping symbolic link tests.\n");
+    }else {
+        ok(ret, "CreateSymbolicLinkW failed\n");
+        ok(pCreateSymbolicLinkW(test_dir_linkW, test_dirW, 1), "CreateSymbolicLinkW failed\n");
+    }
+
+    file = CreateNamedPipeW(pipeW,
+            PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, 2, 1024, 1024,
+            NMPWAIT_USE_DEFAULT_WAIT, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+    perms = 0xdeadbeef;
+    val = p_Stat(pipeW, &perms);
+    todo_wine ok(file_type_regular == val, "_Stat(): expect: regular, got %d\n", val);
+    todo_wine ok(0777 == perms, "_Stat(): perms expect: 0777, got 0%o\n", perms);
+    perms = 0xdeadbeef;
+    val = p_Lstat(pipeW, &perms);
+    ok(file_type_unknown == val, "_Lstat(): expect: unknown, got %d\n", val);
+    ok(0xdeadbeef == perms, "_Lstat(): perms expect: 0xdeadbeef, got %x\n", perms);
+    ok(CloseHandle(file), "CloseHandle\n");
+    file = CreateNamedPipeW(pipeW,
+            PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, 2, 1024, 1024,
+            NMPWAIT_USE_DEFAULT_WAIT, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+    perms = 0xdeadbeef;
+    val = p_Lstat(pipeW, &perms);
+    todo_wine ok(file_type_regular == val, "_Lstat(): expect: regular, got %d\n", val);
+    todo_wine ok(0777 == perms, "_Lstat(): perms expect: 0777, got 0%o\n", perms);
+    ok(CloseHandle(file), "CloseHandle\n");
+
+    for(i=0; i<sizeof(tests)/sizeof(tests[0]); 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 == tests[i].perms, "_Stat(): test %d perms expect: 0%o, got 0%o\n",
+                i+1, tests[i].perms, perms);
+
+        /* 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 == tests[i].perms, "_Lstat(): test %d perms expect: 0%o, got 0%o\n",
+                i+1, tests[i].perms, perms);
+    }
+
+    GetSystemDirectoryW(sys_path, MAX_PATH);
+    perms = 0xdeadbeef;
+    val = p_Stat(sys_path, &perms);
+    ok(file_type_directory == val, "_Stat(): expect: regular, got %d\n", val);
+    ok(0777 == perms, "_Stat(): perms expect: 0777, got 0%o\n", perms);
+
+    if(ret) {
+        todo_wine ok(DeleteFileW(test_f1_linkW), "expect tr2_test_dir/f1_link to exist\n");
+        todo_wine ok(RemoveDirectoryW(test_dir_linkW), "expect tr2_test_dir/dir_link to exist\n");
+    }
+    ok(DeleteFileW(test_f1W), "expect tr2_test_dir/f1 to exist\n");
+    SetFileAttributesW(test_f2W, FILE_ATTRIBUTE_NORMAL);
+    ok(DeleteFileW(test_f2W), "expect tr2_test_dir/f2 to exist\n");
+    ok(RemoveDirectoryW(test_dirW), "expect tr2_test_dir to exist\n");
+
+    ok(SetCurrentDirectoryW(origin_path), "SetCurrentDirectoryW to origin_path failed\n");
+}
+
 START_TEST(msvcp140)
 {
     if(!init()) return;
@@ -705,5 +837,6 @@ START_TEST(msvcp140)
     test_File_size();
     test_Current_get();
     test_Current_set();
+    test_Stat();
     FreeLibrary(msvcp);
 }
diff --git a/dlls/msvcp90/ios.c b/dlls/msvcp90/ios.c
index 9b0b7fef1f..5a26dbd9f5 100644
--- a/dlls/msvcp90/ios.c
+++ b/dlls/msvcp90/ios.c
@@ -349,6 +349,12 @@ enum file_type {
     type_unknown
 };
 
+enum msvcp140_file_type {
+    file_type_not_found = -1, file_type_none, file_type_regular,
+    file_type_directory, file_type_symlink, file_type_block,
+    file_type_character, file_type_fifo, file_type_socket, file_type_unknown
+};
+
 #if _MSVCP_VER >= 110
 #define BASIC_IOS_VTORDISP 1
 #define INIT_BASIC_IOS_VTORDISP(basic_ios) ((int*)basic_ios)[-1] = 0
@@ -15645,6 +15651,36 @@ enum file_type __cdecl tr2_sys__Stat_wchar(WCHAR const* path, int* err_code)
     return (attr & FILE_ATTRIBUTE_DIRECTORY)?directory_file:regular_file;
 }
 
+/* _Stat, msvcp140 version */
+enum msvcp140_file_type __cdecl _Stat(WCHAR const* path, int* permissions)
+{
+    DWORD attr;
+    TRACE("(%s %p)\n", debugstr_w(path), permissions);
+    if(!path) {
+        return file_type_not_found;
+    }
+
+    attr=GetFileAttributesW(path);
+    if(attr == INVALID_FILE_ATTRIBUTES) {
+        enum file_type ret;
+        switch(GetLastError()) {
+            case ERROR_FILE_NOT_FOUND:
+            case ERROR_BAD_NETPATH:
+            case ERROR_INVALID_NAME:
+            case ERROR_BAD_PATHNAME:
+            case ERROR_PATH_NOT_FOUND:
+                ret = file_type_not_found;
+                break;
+            default:
+                ret = file_type_unknown;
+        }
+        return ret;
+    }
+
+    *permissions = (attr & FILE_ATTRIBUTE_READONLY)?0555:0777;
+    return (attr & FILE_ATTRIBUTE_DIRECTORY)?file_type_directory:file_type_regular;
+}
+
 /* ?_Lstat at sys@tr2 at std@@YA?AW4file_type at 123@PB_WAAH at Z */
 /* ?_Lstat at sys@tr2 at std@@YA?AW4file_type at 123@PEB_WAEAH at Z */
 enum file_type __cdecl tr2_sys__Lstat_wchar(WCHAR const* path, int* err_code)
@@ -15652,6 +15688,12 @@ enum file_type __cdecl tr2_sys__Lstat_wchar(WCHAR const* path, int* err_code)
     return tr2_sys__Stat_wchar(path, err_code);
 }
 
+/* _Lstat, msvcp140 version */
+enum msvcp140_file_type __cdecl _Lstat(WCHAR const* path, int* permissions)
+{
+    return _Stat(path, permissions);
+}
+
 /* ??1_Winit at std@@QAE at XZ */
 /* ??1_Winit at std@@QAE at XZ */
 DEFINE_THISCALL_WRAPPER(_Winit_dtor, 4)
-- 
2.13.0




More information about the wine-patches mailing list