msvcp140: Implement _Stat and _Lstat.
Signed-off-by: Stefan Dösinger <stefan@codeweavers.com> Signed-off-by: Piotr Caban <piotr@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
f03b8a42f9
commit
621e96bfdb
|
@ -3669,7 +3669,7 @@
|
||||||
@ stub _Last_write_time
|
@ stub _Last_write_time
|
||||||
@ stub _Link
|
@ stub _Link
|
||||||
@ cdecl _Lock_shared_ptr_spin_lock()
|
@ cdecl _Lock_shared_ptr_spin_lock()
|
||||||
@ stub _Lstat
|
@ cdecl _Lstat(wstr ptr)
|
||||||
@ cdecl _Make_dir(wstr) tr2_sys__Make_dir_wchar
|
@ cdecl _Make_dir(wstr) tr2_sys__Make_dir_wchar
|
||||||
@ cdecl _Mbrtowc(ptr ptr long ptr ptr) _Mbrtowc
|
@ cdecl _Mbrtowc(ptr ptr long ptr ptr) _Mbrtowc
|
||||||
@ stub _Mtx_clear_owner
|
@ stub _Mtx_clear_owner
|
||||||
|
@ -3699,7 +3699,7 @@
|
||||||
@ stub _Set_last_write_time
|
@ stub _Set_last_write_time
|
||||||
@ stub _Sinh
|
@ stub _Sinh
|
||||||
@ extern _Snan _Snan
|
@ extern _Snan _Snan
|
||||||
@ stub _Stat
|
@ cdecl _Stat(wstr ptr)
|
||||||
@ stub _Statvfs
|
@ stub _Statvfs
|
||||||
@ cdecl _Stod(ptr ptr long) _Stod
|
@ cdecl _Stod(ptr ptr long) _Stod
|
||||||
@ cdecl _Stodx(ptr ptr long ptr) _Stodx
|
@ cdecl _Stodx(ptr ptr long ptr) _Stodx
|
||||||
|
|
|
@ -135,6 +135,19 @@ typedef struct {
|
||||||
void *arg;
|
void *arg;
|
||||||
} _Threadpool_chore;
|
} _Threadpool_chore;
|
||||||
|
|
||||||
|
enum file_type {
|
||||||
|
file_not_found = -1,
|
||||||
|
none_file,
|
||||||
|
regular_file,
|
||||||
|
directory_file,
|
||||||
|
symlink_file,
|
||||||
|
block_file,
|
||||||
|
character_file,
|
||||||
|
fifo_file,
|
||||||
|
socket_file,
|
||||||
|
status_unknown
|
||||||
|
};
|
||||||
|
|
||||||
static unsigned int (__cdecl *p__Thrd_id)(void);
|
static unsigned int (__cdecl *p__Thrd_id)(void);
|
||||||
static task_continuation_context* (__thiscall *p_task_continuation_context_ctor)(task_continuation_context*);
|
static task_continuation_context* (__thiscall *p_task_continuation_context_ctor)(task_continuation_context*);
|
||||||
static void (__thiscall *p__ContextCallback__Assign)(_ContextCallback*, void*);
|
static void (__thiscall *p__ContextCallback__Assign)(_ContextCallback*, void*);
|
||||||
|
@ -155,14 +168,20 @@ static void (__cdecl *p__Release_chore)(_Threadpool_chore*);
|
||||||
static MSVCP_bool (__cdecl *p_Current_get)(WCHAR *);
|
static MSVCP_bool (__cdecl *p_Current_get)(WCHAR *);
|
||||||
static MSVCP_bool (__cdecl *p_Current_set)(WCHAR const *);
|
static MSVCP_bool (__cdecl *p_Current_set)(WCHAR const *);
|
||||||
static ULONGLONG (__cdecl *p_File_size)(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_byte)(const WCHAR *src, char *dst);
|
||||||
static int (__cdecl *p_To_wide)(const char *src, WCHAR *dst);
|
static int (__cdecl *p_To_wide)(const char *src, WCHAR *dst);
|
||||||
|
|
||||||
|
static BOOLEAN (WINAPI *pCreateSymbolicLinkW)(const WCHAR *, const WCHAR *, DWORD);
|
||||||
|
|
||||||
static HMODULE msvcp;
|
static HMODULE msvcp;
|
||||||
#define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y)
|
#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)
|
#define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0)
|
||||||
static BOOL init(void)
|
static BOOL init(void)
|
||||||
{
|
{
|
||||||
|
HANDLE hdll;
|
||||||
|
|
||||||
msvcp = LoadLibraryA("msvcp140.dll");
|
msvcp = LoadLibraryA("msvcp140.dll");
|
||||||
if(!msvcp)
|
if(!msvcp)
|
||||||
{
|
{
|
||||||
|
@ -222,9 +241,14 @@ static BOOL init(void)
|
||||||
SET(p_Current_get, "_Current_get");
|
SET(p_Current_get, "_Current_get");
|
||||||
SET(p_Current_set, "_Current_set");
|
SET(p_Current_set, "_Current_set");
|
||||||
SET(p_File_size, "_File_size");
|
SET(p_File_size, "_File_size");
|
||||||
|
SET(p_Lstat, "_Lstat");
|
||||||
|
SET(p_Stat, "_Stat");
|
||||||
SET(p_To_byte, "_To_byte");
|
SET(p_To_byte, "_To_byte");
|
||||||
SET(p_To_wide, "_To_wide");
|
SET(p_To_wide, "_To_wide");
|
||||||
|
|
||||||
|
hdll = GetModuleHandleA("kernel32.dll");
|
||||||
|
pCreateSymbolicLinkW = (void*)GetProcAddress(hdll, "CreateSymbolicLinkW");
|
||||||
|
|
||||||
init_thiscall_thunk();
|
init_thiscall_thunk();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -691,6 +715,130 @@ static void test_Current_set(void)
|
||||||
wine_dbgstr_w(origin_path), wine_dbgstr_w(current_path));
|
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_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 },
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
if(!ret && (!pCreateSymbolicLinkW || GetLastError()==ERROR_PRIVILEGE_NOT_HELD||GetLastError()==ERROR_INVALID_FUNCTION)) {
|
||||||
|
tests[6].ret = tests[7].ret = file_not_found;
|
||||||
|
tests[6].perms = tests[7].perms = 0xdeadbeef;
|
||||||
|
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(regular_file == 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(status_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(regular_file == 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 == 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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSystemDirectoryW(sys_path, MAX_PATH);
|
||||||
|
perms = 0xdeadbeef;
|
||||||
|
val = p_Stat(sys_path, &perms);
|
||||||
|
ok(directory_file == 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)
|
START_TEST(msvcp140)
|
||||||
{
|
{
|
||||||
if(!init()) return;
|
if(!init()) return;
|
||||||
|
@ -705,5 +853,6 @@ START_TEST(msvcp140)
|
||||||
test_File_size();
|
test_File_size();
|
||||||
test_Current_get();
|
test_Current_get();
|
||||||
test_Current_set();
|
test_Current_set();
|
||||||
|
test_Stat();
|
||||||
FreeLibrary(msvcp);
|
FreeLibrary(msvcp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,9 +344,25 @@ struct space_info {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum file_type {
|
enum file_type {
|
||||||
status_unknown, file_not_found, regular_file, directory_file,
|
#if _MSVCP_VER < 140
|
||||||
symlink_file, block_file, character_file, fifo_file, socket_file,
|
status_unknown,
|
||||||
type_unknown
|
file_not_found,
|
||||||
|
#else
|
||||||
|
file_not_found = -1,
|
||||||
|
none_file,
|
||||||
|
#endif
|
||||||
|
regular_file,
|
||||||
|
directory_file,
|
||||||
|
symlink_file,
|
||||||
|
block_file,
|
||||||
|
character_file,
|
||||||
|
fifo_file,
|
||||||
|
socket_file,
|
||||||
|
#if _MSVCP_VER < 140
|
||||||
|
type_unknown,
|
||||||
|
#else
|
||||||
|
status_unknown
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#if _MSVCP_VER >= 110
|
#if _MSVCP_VER >= 110
|
||||||
|
@ -15645,6 +15661,37 @@ enum file_type __cdecl tr2_sys__Stat_wchar(WCHAR const* path, int* err_code)
|
||||||
return (attr & FILE_ATTRIBUTE_DIRECTORY)?directory_file:regular_file;
|
return (attr & FILE_ATTRIBUTE_DIRECTORY)?directory_file:regular_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* _Stat, msvcp140 version */
|
||||||
|
enum file_type __cdecl _Stat(WCHAR const* path, int* permissions)
|
||||||
|
{
|
||||||
|
DWORD attr;
|
||||||
|
TRACE("(%s %p)\n", debugstr_w(path), permissions);
|
||||||
|
if(!path) {
|
||||||
|
return file_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_not_found;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = status_unknown;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions)
|
||||||
|
*permissions = (attr & FILE_ATTRIBUTE_READONLY) ? 0555 : 0777;
|
||||||
|
return (attr & FILE_ATTRIBUTE_DIRECTORY) ? directory_file : regular_file;
|
||||||
|
}
|
||||||
|
|
||||||
/* ?_Lstat@sys@tr2@std@@YA?AW4file_type@123@PB_WAAH@Z */
|
/* ?_Lstat@sys@tr2@std@@YA?AW4file_type@123@PB_WAAH@Z */
|
||||||
/* ?_Lstat@sys@tr2@std@@YA?AW4file_type@123@PEB_WAEAH@Z */
|
/* ?_Lstat@sys@tr2@std@@YA?AW4file_type@123@PEB_WAEAH@Z */
|
||||||
enum file_type __cdecl tr2_sys__Lstat_wchar(WCHAR const* path, int* err_code)
|
enum file_type __cdecl tr2_sys__Lstat_wchar(WCHAR const* path, int* err_code)
|
||||||
|
@ -15652,6 +15699,12 @@ enum file_type __cdecl tr2_sys__Lstat_wchar(WCHAR const* path, int* err_code)
|
||||||
return tr2_sys__Stat_wchar(path, err_code);
|
return tr2_sys__Stat_wchar(path, err_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* _Lstat, msvcp140 version */
|
||||||
|
enum file_type __cdecl _Lstat(WCHAR const* path, int* permissions)
|
||||||
|
{
|
||||||
|
return _Stat(path, permissions);
|
||||||
|
}
|
||||||
|
|
||||||
/* ??1_Winit@std@@QAE@XZ */
|
/* ??1_Winit@std@@QAE@XZ */
|
||||||
/* ??1_Winit@std@@QAE@XZ */
|
/* ??1_Winit@std@@QAE@XZ */
|
||||||
DEFINE_THISCALL_WRAPPER(_Winit_dtor, 4)
|
DEFINE_THISCALL_WRAPPER(_Winit_dtor, 4)
|
||||||
|
|
Loading…
Reference in New Issue