From 75f520b3a9a877c3f75bb6bae05e4e1b997af52a Mon Sep 17 00:00:00 2001 From: Gijs Vermeulen Date: Mon, 4 Feb 2019 15:16:39 +0100 Subject: [PATCH] kernel32: Implement CreateSymbolicLink. Signed-off-by: Gijs Vermeulen --- dlls/kernel32/path.c | 72 ++++++++++++++++++++++++++++++++-- dlls/msvcp120/tests/msvcp120.c | 51 ++++++++++++------------ 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index cf1c768970..cb8540389e 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -2079,8 +2079,55 @@ WCHAR * CDECL wine_get_dos_file_name( LPCSTR str ) */ BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags) { - FIXME("(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags); - return TRUE; + NTSTATUS status, temp; + UNICODE_STRING nt_dest, nt_source; + ANSI_STRING unix_dest, unix_source; + BOOL ret = FALSE; + + TRACE("(%s, %s, %d)\n", debugstr_w(link), debugstr_w(target), flags); + + nt_dest.Buffer = nt_source.Buffer = NULL; + if (!RtlDosPathNameToNtPathName_U( link, &nt_dest, NULL, NULL ) || + !RtlDosPathNameToNtPathName_U( target, &nt_source, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto err; + } + + unix_source.Buffer = unix_dest.Buffer = NULL; + temp = wine_nt_to_unix_file_name( &nt_source, &unix_source, FILE_OPEN_IF, FALSE ); + status = wine_nt_to_unix_file_name( &nt_dest, &unix_dest, FILE_CREATE, FALSE ); + if (!status) /* destination must not exist */ + status = STATUS_OBJECT_NAME_EXISTS; + else if (status == STATUS_NO_SUCH_FILE) + status = STATUS_SUCCESS; + + if (status) + SetLastError( RtlNtStatusToDosError(status) ); + else + { + if(temp != STATUS_NO_SUCH_FILE && GetFileAttributesW(target) & FILE_ATTRIBUTE_DIRECTORY && strstr(unix_dest.Buffer, unix_source.Buffer)) + { + FIXME("Symlinking a directory inside that directory is not supported.\n"); + ret = TRUE; + } + else if (!symlink( unix_source.Buffer, unix_dest.Buffer )) + { + TRACE("Symlinked '%s' to '%s'\n", debugstr_a( unix_dest.Buffer ), + debugstr_a( unix_source.Buffer )); + ret = TRUE; + } + else + FILE_SetDosError(); + } + + RtlFreeAnsiString( &unix_source ); + RtlFreeAnsiString( &unix_dest ); + +err: + RtlFreeUnicodeString( &nt_source ); + RtlFreeUnicodeString( &nt_dest ); + return ret; } /************************************************************************* @@ -2088,8 +2135,25 @@ BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags) */ BOOLEAN WINAPI CreateSymbolicLinkA(LPCSTR link, LPCSTR target, DWORD flags) { - FIXME("(%s %s %d): stub\n", debugstr_a(link), debugstr_a(target), flags); - return TRUE; + WCHAR *sourceW, *destW; + BOOL res; + + if (!(sourceW = FILE_name_AtoW( target, TRUE ))) + { + return FALSE; + } + if (!(destW = FILE_name_AtoW( link, TRUE ))) + { + HeapFree( GetProcessHeap(), 0, sourceW ); + return FALSE; + } + + res = CreateSymbolicLinkW( destW, sourceW, flags ); + + HeapFree( GetProcessHeap(), 0, sourceW ); + HeapFree( GetProcessHeap(), 0, destW ); + + return res; } /************************************************************************* diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c index 70fc0c13dc..0aa5225295 100644 --- a/dlls/msvcp120/tests/msvcp120.c +++ b/dlls/msvcp120/tests/msvcp120.c @@ -1648,7 +1648,7 @@ static void test_tr2_sys__Stat(void) { "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\\f1_link" , regular_file, ERROR_SUCCESS, FALSE }, { "tr2_test_dir\\dir_link", directory_file, ERROR_SUCCESS, TRUE }, }; WCHAR testW[] = {'t','r','2','_','t','e','s','t','_','d','i','r',0}; @@ -1718,7 +1718,7 @@ 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"); + 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"), "expect tr2_test_dir/f1 to exist\n"); @@ -1954,14 +1954,14 @@ static void test_tr2_sys__Symlink(void) 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 }, + { "f1", "f1_link", ERROR_SUCCESS, TRUE }, + { "f1", "tr2_test_dir\\f1_link", ERROR_SUCCESS, TRUE }, + { "tr2_test_dir\\f1_link", "tr2_test_dir\\f1_link_link", ERROR_SUCCESS, TRUE }, { "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", "not_exist_dir\\f1_link", ERROR_PATH_NOT_FOUND, FALSE } }; ret = p_tr2_sys__Make_dir("tr2_test_dir"); @@ -1986,18 +1986,21 @@ 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))); + { + todo_wine_if(tests[i].is_todo) + 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"); + ok(DeleteFileA("tr2_test_dir/f1_link_link"), "expect tr2_test_dir/f1_link_link to exist\n"); + ok(DeleteFileA("tr2_test_dir/f1_link"), "expect tr2_test_dir/f1_link to exist\n"); + ok(DeleteFileA("f1_link"), "expect f1_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"); + todo_wine ok(DeleteFileA("dir_link"), "expect dir_link to exist, got %d\n", GetLastError()); + ok(DeleteFileA("f1"), "expect f1 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); } @@ -2011,15 +2014,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); @@ -2048,9 +2050,8 @@ static void test_tr2_sys__Unlink(void) for(i=0; i