2019-02-04 16:06:09 -08:00
|
|
|
From 75f520b3a9a877c3f75bb6bae05e4e1b997af52a Mon Sep 17 00:00:00 2001
|
2019-01-29 21:39:10 -08:00
|
|
|
From: Gijs Vermeulen <gijsvrm@gmail.com>
|
2019-02-04 16:06:09 -08:00
|
|
|
Date: Mon, 4 Feb 2019 15:16:39 +0100
|
|
|
|
Subject: [PATCH] kernel32: Implement CreateSymbolicLink.
|
2019-01-29 21:39:10 -08:00
|
|
|
|
|
|
|
Signed-off-by: Gijs Vermeulen <gijsvrm@gmail.com>
|
|
|
|
---
|
2019-02-04 16:06:09 -08:00
|
|
|
dlls/kernel32/path.c | 72 ++++++++++++++++++++++++++++++++--
|
|
|
|
dlls/msvcp120/tests/msvcp120.c | 51 ++++++++++++------------
|
|
|
|
2 files changed, 94 insertions(+), 29 deletions(-)
|
2019-01-29 21:39:10 -08:00
|
|
|
|
|
|
|
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
|
2019-02-04 16:06:09 -08:00
|
|
|
index cf1c768970..cb8540389e 100644
|
2019-01-29 21:39:10 -08:00
|
|
|
--- a/dlls/kernel32/path.c
|
|
|
|
+++ b/dlls/kernel32/path.c
|
2019-02-04 16:06:09 -08:00
|
|
|
@@ -2079,8 +2079,55 @@ WCHAR * CDECL wine_get_dos_file_name( LPCSTR str )
|
2019-01-29 21:39:10 -08:00
|
|
|
*/
|
|
|
|
BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags)
|
|
|
|
{
|
|
|
|
- FIXME("(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags);
|
|
|
|
- return TRUE;
|
2019-02-04 16:06:09 -08:00
|
|
|
+ NTSTATUS status, temp;
|
2019-01-29 21:39:10 -08:00
|
|
|
+ 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;
|
2019-02-04 16:06:09 -08:00
|
|
|
+ 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;
|
2019-01-29 21:39:10 -08:00
|
|
|
+
|
|
|
|
+ if (status)
|
2019-02-04 16:06:09 -08:00
|
|
|
+ SetLastError( RtlNtStatusToDosError(status) );
|
|
|
|
+ else
|
2019-01-29 21:39:10 -08:00
|
|
|
+ {
|
2019-02-04 16:06:09 -08:00
|
|
|
+ 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 ),
|
2019-01-29 21:39:10 -08:00
|
|
|
+ debugstr_a( unix_source.Buffer ));
|
2019-02-04 16:06:09 -08:00
|
|
|
+ ret = TRUE;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ FILE_SetDosError();
|
2019-01-29 21:39:10 -08:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RtlFreeAnsiString( &unix_source );
|
|
|
|
+ RtlFreeAnsiString( &unix_dest );
|
|
|
|
+
|
|
|
|
+err:
|
|
|
|
+ RtlFreeUnicodeString( &nt_source );
|
|
|
|
+ RtlFreeUnicodeString( &nt_dest );
|
|
|
|
+ return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
2019-02-04 16:06:09 -08:00
|
|
|
@@ -2088,8 +2135,25 @@ BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags)
|
2019-01-29 21:39:10 -08:00
|
|
|
*/
|
|
|
|
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
|
2019-02-04 16:06:09 -08:00
|
|
|
index 70fc0c13dc..0aa5225295 100644
|
2019-01-29 21:39:10 -08:00
|
|
|
--- 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)
|
2019-02-04 16:06:09 -08:00
|
|
|
+ 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)));
|
2019-01-29 21:39:10 -08:00
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
- 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");
|
2019-02-04 16:06:09 -08:00
|
|
|
+ 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());
|
2019-01-29 21:39:10 -08:00
|
|
|
+ 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);
|
2019-02-04 16:06:09 -08:00
|
|
|
@@ -2048,9 +2050,8 @@ static void test_tr2_sys__Unlink(void)
|
2019-01-29 21:39:10 -08:00
|
|
|
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",
|
2019-02-04 16:06:09 -08:00
|
|
|
- i+1, tests[i].last_error, ret);
|
2019-01-29 21:39:10 -08:00
|
|
|
+ ok(ret == tests[i].last_error, "tr2_sys__Unlink(): test %d expect: %d, got %d\n",
|
2019-02-04 16:06:09 -08:00
|
|
|
+ i+1, tests[i].last_error, ret);
|
2019-01-29 21:39:10 -08:00
|
|
|
ok(errno == 0xdeadbeef, "tr2_sys__Unlink(): test %d errno expect: 0xdeadbeef, got %d\n", i+1, ret);
|
|
|
|
}
|
2019-02-04 16:06:09 -08:00
|
|
|
|
2019-01-29 21:39:10 -08:00
|
|
|
--
|
|
|
|
2.20.1
|
|
|
|
|