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) ...@@ -5562,6 +5562,35 @@ static void test_reparse_points(void)
/* Delete the junction point directory and create a blank slate for symlink tests */ /* Delete the junction point directory and create a blank slate for symlink tests */
bret = RemoveDirectoryW(reparse_path); bret = RemoveDirectoryW(reparse_path);
ok(bret, "Failed to delete junction point!\n"); 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); bret = CreateDirectoryW(reparse_path, NULL);
ok(bret, "Failed to create junction point directory.\n"); ok(bret, "Failed to create junction point directory.\n");
dwret = GetFileAttributesW(reparse_path); dwret = GetFileAttributesW(reparse_path);
......
...@@ -5988,6 +5988,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer) ...@@ -5988,6 +5988,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
int relative_offset = 0; int relative_offset = 0;
UNICODE_STRING nt_dest; UNICODE_STRING nt_dest;
int dest_len, offset; int dest_len, offset;
BOOL is_dir = TRUE;
NTSTATUS status; NTSTATUS status;
struct stat st; struct stat st;
WCHAR *dest; WCHAR *dest;
...@@ -6121,7 +6122,6 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer) ...@@ -6121,7 +6122,6 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
/* Encode the type (file or directory) if NT symlink */ /* Encode the type (file or directory) if NT symlink */
if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
{ {
BOOL is_dir;
if (fstat( dest_fd, &st ) == -1) if (fstat( dest_fd, &st ) == -1)
{ {
status = errno_to_status( errno ); status = errno_to_status( errno );
...@@ -6155,8 +6155,11 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer) ...@@ -6155,8 +6155,11 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
/* Atomically move the link into position */ /* Atomically move the link into position */
if (!renameat2( -1, tmplink, -1, unix_src, RENAME_EXCHANGE )) if (!renameat2( -1, tmplink, -1, unix_src, RENAME_EXCHANGE ))
{ {
/* success: link and folder have switched locations */ /* success: link and folder/file have switched locations */
rmdir( tmplink ); /* remove the folder (at link location) */ if (is_dir)
rmdir( tmplink ); /* remove the folder (at link location) */
else
unlink( tmplink ); /* remove the file (at link location) */
} }
else if (errno == ENOSYS) else if (errno == ENOSYS)
{ {
...@@ -6375,6 +6378,7 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer) ...@@ -6375,6 +6378,7 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
char tmpdir[PATH_MAX], tmpfile[PATH_MAX], *d; char tmpdir[PATH_MAX], tmpfile[PATH_MAX], *d;
BOOL tempdir_created = FALSE; BOOL tempdir_created = FALSE;
int dest_fd, needs_close; int dest_fd, needs_close;
BOOL is_dir = TRUE;
NTSTATUS status; NTSTATUS status;
char *unix_name; char *unix_name;
struct stat st; struct stat st;
...@@ -6387,12 +6391,13 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer) ...@@ -6387,12 +6391,13 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
TRACE( "Deleting symlink %s\n", unix_name ); 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) if (fstat( dest_fd, &st ) == -1)
{ {
status = errno_to_status( errno ); status = errno_to_status( errno );
goto cleanup; goto cleanup;
} }
is_dir = S_ISDIR(st.st_mode);
strcpy( tmpdir, unix_name ); strcpy( tmpdir, unix_name );
d = dirname( tmpdir); d = dirname( tmpdir);
if (d != tmpdir) strcpy( tmpdir, d ); if (d != tmpdir) strcpy( tmpdir, d );
...@@ -6405,11 +6410,21 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer) ...@@ -6405,11 +6410,21 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
tempdir_created = TRUE; tempdir_created = TRUE;
strcpy( tmpfile, tmpdir ); strcpy( tmpfile, tmpdir );
strcat( tmpfile, "/tmpfile" ); strcat( tmpfile, "/tmpfile" );
if (mkdir( tmpfile, st.st_mode )) if (is_dir && mkdir( tmpfile, st.st_mode ))
{ {
status = errno_to_status( errno ); status = errno_to_status( errno );
goto cleanup; 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) */ /* attemp to retain the ownership (if possible) */
lchown( tmpfile, st.st_uid, st.st_gid ); lchown( tmpfile, st.st_uid, st.st_gid );
/* Atomically move the directory into position */ /* 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