[PATCH 4/4] msvcp140: Implement _Stat and _Lstat.
Stefan Dösinger
stefan at codeweavers.com
Tue Jul 11 05:30:18 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>
---
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 | 134 ++++++++++++++++++++++++++++++++++++++++-
dlls/msvcp90/ios.c | 42 +++++++++++++
3 files changed, 177 insertions(+), 3 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 f4e4964405..04e1162491 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)
{
@@ -221,10 +233,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 +707,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 +836,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 f3034bff0d..cc24aa1bcf 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
@@ -15649,6 +15655,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)
@@ -15656,6 +15692,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