From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/tests/file.c | 87 +++++++++++++++++++++++------------------ dlls/ntdll/unix/file.c | 52 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 37 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index c50ba97cd04..0b4c7503511 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6701,18 +6701,21 @@ static void test_reparse_points(void) ret = LookupPrivilegeValueA( NULL, "SeCreateSymbolicLinkPrivilege", &luid ); todo_wine ok( ret == TRUE, "got error %lu\n", GetLastError() );
- privs.PrivilegeCount = 1; - privs.Privileges[0].Luid = luid; - privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - ret = AdjustTokenPrivileges( token, FALSE, &privs, 0, NULL, NULL ); - ok( ret == TRUE, "got error %lu\n", GetLastError() ); - if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + if (ret) { - todo_wine win_skip("Insufficient permissions to perform symlink tests.\n"); + privs.PrivilegeCount = 1; + privs.Privileges[0].Luid = luid; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + ret = AdjustTokenPrivileges( token, FALSE, &privs, 0, NULL, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) + { + win_skip("Insufficient permissions to perform symlink tests.\n"); + CloseHandle( token ); + goto out; + } CloseHandle( token ); - goto out; } - CloseHandle( token );
RtlInitUnicodeString( &nameW, L"testreparse_filelink" ); status = NtCreateFile( &handle, GENERIC_ALL, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -6725,7 +6728,7 @@ static void test_reparse_points(void)
status = NtCreateFile( &handle2, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0, NULL, 0 ); - ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status );
data_size = init_reparse_symlink( &data, L"testreparse_dir", SYMLINK_FLAG_RELATIVE ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, data_size, NULL, 0 ); @@ -6733,7 +6736,7 @@ static void test_reparse_points(void)
status = NtCreateFile( &handle2, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0, NULL, 0 ); - ok( status == STATUS_FILE_IS_A_DIRECTORY || broken(!status) /* < Win10 1709 */, "got %#lx\n", status ); + todo_wine ok( status == STATUS_FILE_IS_A_DIRECTORY || broken(!status) /* < Win10 1709 */, "got %#lx\n", status ); if (!status) NtClose( handle2 );
data_size = init_reparse_symlink( &data, L"testreparse_file\", SYMLINK_FLAG_RELATIVE ); @@ -6796,7 +6799,7 @@ static void test_reparse_points(void) * in the file. The status here is rather misleading, of course. */ data->ReparseDataLength = data_size - offsetof( REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, data_size, NULL, 0 ); - ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status );
SetFilePointer( handle, 0, 0, FILE_BEGIN ); ret = SetEndOfFile( handle ); @@ -6870,20 +6873,20 @@ static void test_reparse_points(void) ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); status = NtQueryInformationFile( handle2, &io, &tag_info, sizeof(tag_info), FileAttributeTagInformation ); ok( !status, "got %#lx\n", status ); - ok( tag_info.FileAttributes == (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT), + todo_wine ok( tag_info.FileAttributes == (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT), "got attributes %#lx\n", tag_info.FileAttributes ); - ok( tag_info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got tag %#lx\n", tag_info.ReparseTag ); + todo_wine ok( tag_info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got tag %#lx\n", tag_info.ReparseTag ); status = NtQueryInformationFile( handle2, &io, &std_info, sizeof(std_info), FileStandardInformation ); ok( !status, "got %#lx\n", status ); - ok( !std_info.AllocationSize.QuadPart, "got size %#I64x\n", std_info.AllocationSize.QuadPart ); - ok( !std_info.EndOfFile.QuadPart, "got eof %#I64x\n", std_info.EndOfFile.QuadPart ); + todo_wine ok( !std_info.AllocationSize.QuadPart, "got size %#I64x\n", std_info.AllocationSize.QuadPart ); + todo_wine ok( !std_info.EndOfFile.QuadPart, "got eof %#I64x\n", std_info.EndOfFile.QuadPart ); ok( std_info.NumberOfLinks == 2, "got %lu links\n", std_info.NumberOfLinks ); ok( !std_info.Directory, "got directory %u\n", std_info.Directory ); status = NtQueryInformationFile( handle, &io, &internal_info, sizeof(internal_info), FileInternalInformation ); ok( !status, "got %#lx\n", status ); status = NtQueryInformationFile( handle2, &io, &internal_info2, sizeof(internal_info2), FileInternalInformation ); ok( !status, "got %#lx\n", status ); - ok( internal_info.IndexNumber.QuadPart == internal_info2.IndexNumber.QuadPart, "got ids %#I64x vs %#I64x\n", + todo_wine ok( internal_info.IndexNumber.QuadPart == internal_info2.IndexNumber.QuadPart, "got ids %#I64x vs %#I64x\n", internal_info.IndexNumber.QuadPart, internal_info2.IndexNumber.QuadPart ); CloseHandle( handle2 );
@@ -6925,11 +6928,20 @@ static void test_reparse_points(void)
handle2 = CreateFileW( path, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0 ); - ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); - ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); + todo_wine ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); + todo_wine ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); + if (handle2 != INVALID_HANDLE_VALUE) CloseHandle( handle2 );
ret = MoveFileW( path2, path ); - ok( ret == TRUE, "got error %lu\n", GetLastError() ); + todo_wine ok( ret == TRUE, "got error %lu\n", GetLastError() ); + if (!ret) + { + /* undo what we incorrectly did above */ + WCHAR target[MAX_PATH]; + swprintf( target, ARRAY_SIZE(target), L"%stestreparse_file", temp_path ); + ret = MoveFileW( path2, target ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + }
handle2 = CreateFileW( path2, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, 0 ); ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); @@ -6950,15 +6962,15 @@ static void test_reparse_points(void) ok( ret == TRUE, "got error %lu\n", GetLastError() );
ret = GetFileAttributesW( path ); - ok( ret == (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_REPARSE_POINT), + todo_wine ok( ret == (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_REPARSE_POINT), "got attributes %#x\n", ret );
ret = GetFileAttributesW( path2 ); - ok( ret == FILE_ATTRIBUTE_ARCHIVE, "got attributes %#x\n", ret ); + todo_wine ok( ret == FILE_ATTRIBUTE_ARCHIVE, "got attributes %#x\n", ret );
status = NtQueryAttributesFile( &attr, &basic_info ); - ok( !status, "got %#lx\n", status ); - ok( basic_info.FileAttributes == (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_REPARSE_POINT), + todo_wine ok( !status, "got %#lx\n", status ); + todo_wine ok( basic_info.FileAttributes == (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_REPARSE_POINT), "got attributes %#lx\n", basic_info.FileAttributes );
/* Path names. */ @@ -6972,8 +6984,9 @@ static void test_reparse_points(void) /* ReOpenFile(). */
handle2 = ReOpenFile( handle, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0 ); - ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); - ok( GetLastError() == ERROR_INVALID_REPARSE_DATA, "got error %lu\n", GetLastError() ); + todo_wine ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); + todo_wine ok( GetLastError() == ERROR_INVALID_REPARSE_DATA, "got error %lu\n", GetLastError() ); + if (handle2 != INVALID_HANDLE_VALUE) CloseHandle( handle2 ); handle2 = ReOpenFile( handle, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_FLAG_OPEN_REPARSE_POINT ); ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); CloseHandle( handle2 ); @@ -7042,7 +7055,7 @@ static void test_reparse_points(void)
RtlInitUnicodeString( &nameW, L"testreparse_dirlink\file" ); status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( !status, "got %#lx\n", status ); + todo_wine ok( !status, "got %#lx\n", status ); NtClose( handle2 );
data_size = init_reparse_symlink( &data, L".\testreparse_dir\..\.\testreparse_dir2\.", SYMLINK_FLAG_RELATIVE ); @@ -7052,13 +7065,13 @@ static void test_reparse_points(void) RtlInitUnicodeString( &nameW, L"testreparse_dirlink" ); status = NtCreateFile( &handle2, GENERIC_ALL, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status );
status = NtCreateFile( &handle2, GENERIC_ALL, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( !status, "got %#lx\n", status ); + todo_wine ok( !status, "got %#lx\n", status ); status = NtSetInformationFile( handle2, &io, &fdi, sizeof(fdi), FileDispositionInformation ); - ok( !status, "got %#lx\n", status ); + todo_wine ok( !status, "got %#lx\n", status ); NtClose( handle2 );
data_size = init_reparse_symlink( &data, L"./testreparse_dir", SYMLINK_FLAG_RELATIVE ); @@ -7080,11 +7093,11 @@ static void test_reparse_points(void) ok( !status, "got %#lx\n", status );
status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status );
data_size = init_reparse_symlink( &data, L"", SYMLINK_FLAG_RELATIVE ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, data_size, NULL, 0 ); - ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status );
data_size = init_reparse_symlink( &data, L"..", SYMLINK_FLAG_RELATIVE ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, data_size, NULL, 0 ); @@ -7092,7 +7105,7 @@ static void test_reparse_points(void)
RtlInitUnicodeString( &nameW, L"testreparse_dirlink\" ); status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( !status, "got %#lx\n", status ); + todo_wine ok( !status, "got %#lx\n", status ); NtClose( handle2 );
/* Try to unwind past the drive root. */ @@ -7102,7 +7115,7 @@ static void test_reparse_points(void)
RtlInitUnicodeString( &nameW, L"testreparse_dirlink\" ); status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status );
/* Create an absolute symlink. */
@@ -7153,7 +7166,7 @@ static void test_reparse_points(void) ok( !status, "got %#lx\n", status );
status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status );
swprintf( path, ARRAY_SIZE(path), L".\testreparse_dir2", temp_path ); data_size = init_reparse_symlink( &data, path, 0 ); @@ -7161,7 +7174,7 @@ static void test_reparse_points(void) ok( !status, "got %#lx\n", status );
status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_NAME_INVALID, "got %#lx\n", status );
swprintf( path, ARRAY_SIZE(path), L"\??\%s.\testreparse_dir2", temp_path ); data_size = init_reparse_symlink( &data, path, 0 ); @@ -7169,7 +7182,7 @@ static void test_reparse_points(void) ok( !status, "got %#lx\n", status );
status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "got %#lx\n", status ); + todo_wine ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "got %#lx\n", status );
swprintf( path, ARRAY_SIZE(path), L"\??\%stestreparse_dir2\..\testreparse_dir2", temp_path ); data_size = init_reparse_symlink( &data, path, 0 ); diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 3c59f2c63e9..01e9c1bb9ed 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -3972,6 +3972,58 @@ static NTSTATUS resolve_reparse_point( int fd, int root_fd, OBJECT_ATTRIBUTES *a
switch (data->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: + { + const WCHAR *target = data->SymbolicLinkReparseBuffer.PathBuffer + + data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); + USHORT target_len = data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + + if (data->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) + { + WCHAR *new_nt_name; + + TRACE( "target %s\n", debugstr_wn(target, target_len) ); + + if (!target_len) + { + free( data ); + return STATUS_IO_REPARSE_DATA_INVALID; + } + + if (!(new_nt_name = malloc( (nt_pos + target_len + 1 + remainder_len + 1) * sizeof(WCHAR) ))) + { + free( data ); + return STATUS_NO_MEMORY; + } + + memcpy( new_nt_name, name, nt_pos * sizeof(WCHAR) ); + memcpy( new_nt_name + nt_pos, target, target_len * sizeof(WCHAR) ); + if (remainder_len) + { + if (target[target_len - 1] != '\') + new_nt_name[nt_pos + target_len++] = '\'; + memcpy( new_nt_name + nt_pos + target_len, remainder, remainder_len * sizeof(WCHAR) ); + } + new_nt_name[nt_pos + target_len + remainder_len] = 0; + + free( nt_name->Buffer ); + nt_name->Buffer = new_nt_name; + nt_name->Length = (nt_pos + target_len + remainder_len) * sizeof(WCHAR); + nt_name->MaximumLength = nt_name->Length + sizeof(WCHAR); + attr->ObjectName = nt_name; + + status = lookup_unix_name( root_fd, attr, nt_name, nt_pos, unix_name, unix_len, pos, + disposition, open_reparse, is_unix, reparse_count ); + } + else + { + status = resolve_absolute_reparse_point( target, target_len, attr, nt_name, + remainder, remainder_len, unix_name, + disposition, open_reparse, reparse_count ); + } + break; + } + case IO_REPARSE_TAG_MOUNT_POINT: { const WCHAR *target = data->MountPointReparseBuffer.PathBuffer