Commit 1080b7ce authored by Erich E. Hoover's avatar Erich E. Hoover Committed by Vitaly Lipatov

ntdll: Add support for file symlinks.

parent 4fa44192
......@@ -5562,6 +5562,35 @@ static void test_reparse_points(void)
/* Delete the junction point directory and create a blank slate for symlink tests */
bret = RemoveDirectoryW(reparse_path);
ok(bret, "Failed to delete junction point!\n");
/* Create the file symlink */
HeapFree(GetProcessHeap(), 0, buffer);
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
ok(handle != INVALID_HANDLE_VALUE, "Failed to create symlink file.\n");
dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
ok(dwret == STATUS_SUCCESS, "Failed to get symlink file's attributes (0x%x).\n", dwret);
buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, 0, &buffer);
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
CloseHandle(handle);
/* Check deleting a file symlink as if it were a directory */
bret = RemoveDirectoryW(reparse_path);
todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
err = GetLastError();
todo_wine ok(err == ERROR_DIRECTORY,
"Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
ERROR_DIRECTORY, err);
dwret = GetFileAttributesW(reparse_path);
todo_wine ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
/* Delete the symlink as a file */
bret = DeleteFileW(reparse_path);
todo_wine ok(bret, "Failed to delete symlink as a file!\n");
/* Create a blank slate for directory symlink tests */
bret = CreateDirectoryW(reparse_path, NULL);
ok(bret, "Failed to create junction point directory.\n");
dwret = GetFileAttributesW(reparse_path);
......
......@@ -5988,6 +5988,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
int relative_offset = 0;
UNICODE_STRING nt_dest;
int dest_len, offset;
BOOL is_dir = TRUE;
NTSTATUS status;
struct stat st;
WCHAR *dest;
......@@ -6121,7 +6122,6 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
/* Encode the type (file or directory) if NT symlink */
if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
{
BOOL is_dir;
if (fstat( dest_fd, &st ) == -1)
{
status = errno_to_status( errno );
......@@ -6155,8 +6155,11 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
/* Atomically move the link into position */
if (!renameat2( -1, tmplink, -1, unix_src, RENAME_EXCHANGE ))
{
/* success: link and folder have switched locations */
rmdir( tmplink ); /* remove the folder (at link location) */
/* success: link and folder/file have switched locations */
if (is_dir)
rmdir( tmplink ); /* remove the folder (at link location) */
else
unlink( tmplink ); /* remove the file (at link location) */
}
else if (errno == ENOSYS)
{
......@@ -6375,6 +6378,7 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
char tmpdir[PATH_MAX], tmpfile[PATH_MAX], *d;
BOOL tempdir_created = FALSE;
int dest_fd, needs_close;
BOOL is_dir = TRUE;
NTSTATUS status;
char *unix_name;
struct stat st;
......@@ -6387,12 +6391,13 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
TRACE( "Deleting symlink %s\n", unix_name );
/* Produce the directory in a temporary location in the same folder */
/* Produce the file/directory in a temporary location in the same folder */
if (fstat( dest_fd, &st ) == -1)
{
status = errno_to_status( errno );
goto cleanup;
}
is_dir = S_ISDIR(st.st_mode);
strcpy( tmpdir, unix_name );
d = dirname( tmpdir);
if (d != tmpdir) strcpy( tmpdir, d );
......@@ -6405,11 +6410,21 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
tempdir_created = TRUE;
strcpy( tmpfile, tmpdir );
strcat( tmpfile, "/tmpfile" );
if (mkdir( tmpfile, st.st_mode ))
if (is_dir && mkdir( tmpfile, st.st_mode ))
{
status = errno_to_status( errno );
goto cleanup;
}
else if (!is_dir)
{
int fd = open( tmpfile, O_CREAT|O_WRONLY|O_TRUNC, st.st_mode );
if (fd < 0)
{
status = errno_to_status( errno );
goto cleanup;
}
close( fd );
}
/* attemp to retain the ownership (if possible) */
lchown( tmpfile, st.st_uid, st.st_gid );
/* Atomically move the directory into position */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment