Added patches for support of FileLinkInformation (fixes Wine Staging Bug #297).

This commit is contained in:
Sebastian Lackner 2015-06-03 17:07:24 +02:00
parent 26d8a27276
commit 0d67dee015
7 changed files with 1152 additions and 2 deletions

View File

@ -39,12 +39,13 @@ Wine. All those differences are also documented on the
Included bug fixes and improvements
-----------------------------------
**Bug fixes and features included in the next upcoming release [5]:**
**Bug fixes and features included in the next upcoming release [6]:**
* Add implementation for kernel32.GetNumaProcessorNode ([Wine Bug #38660](https://bugs.winehq.org/show_bug.cgi?id=38660))
* Implement mscoree._CorValidateImage for mono runtime ([Wine Bug #38662](https://bugs.winehq.org/show_bug.cgi?id=38662))
* Implement proper handling of CLI .NET images in Wine library loader ([Wine Bug #38661](https://bugs.winehq.org/show_bug.cgi?id=38661))
* Multiple applications needs better NtQueryInformationJobObject stub ([Wine Bug #38658](https://bugs.winehq.org/show_bug.cgi?id=38658))
* Support for NtSetInformationFile class FileLinkInformation
* Support for NtSetInformationFile class FileRenameInformation ([Wine Bug #30399](https://bugs.winehq.org/show_bug.cgi?id=30399))

2
debian/changelog vendored
View File

@ -11,6 +11,8 @@ wine-staging (1.7.45) UNRELEASED; urgency=low
* Added patch to fix opening a readonly file with FILE_WRITE_ATTRIBUTES access
(fixes Wine Staging Bug #298).
* Added patch to silence test failures in ntdll/directory tests.
* Added patches for support of FileLinkInformation (fixes Wine Staging Bug
#297).
* Removed patch to fix NULL pointer dereference in get_frame_by_name
(identical patch accepted upstream).
-- Sebastian Lackner <sebastian@fds-team.de> Sun, 31 May 2015 14:46:37 +0200

View File

@ -0,0 +1,30 @@
From b4838252600fd8b4325b1a7d5753a84df40ef131 Mon Sep 17 00:00:00 2001
From: Zhaonan Liang <bt2517@126.com>
Date: Tue, 28 Apr 2015 19:06:31 +0800
Subject: include: Add declaration for FILE_LINK_INFORMATION.
---
include/winternl.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/include/winternl.h b/include/winternl.h
index a84c6d4..d208537 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -567,6 +567,13 @@ typedef struct _FILE_RENAME_INFORMATION {
WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
+typedef struct _FILE_LINK_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION;
+
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
--
2.4.2

View File

@ -0,0 +1,730 @@
From 3049a4cb5eaa0e35e2ce1621a4c090c1fdc13a57 Mon Sep 17 00:00:00 2001
From: Qian Hong <qhong@codeweavers.com>
Date: Wed, 3 Jun 2015 17:41:59 +0800
Subject: ntdll/tests: Add tests for FileLinkInformation class.
Mostly copy and paste from Sebastian Lackner's FileNameInformation class
tests, credit to Sebastian.
---
dlls/ntdll/tests/file.c | 698 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 698 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 8072a15..c985a03 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2149,6 +2149,703 @@ static void test_file_rename_information(void)
delete_object( newpath );
}
+static void test_file_link_information(void)
+{
+ static const WCHAR foo_txt[] = {'\\','f','o','o','.','t','x','t',0};
+ static const WCHAR foo[] = {'f','o','o',0};
+ WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16], *filename, *p;
+ UNICODE_STRING name_str;
+ HANDLE handle, handle2;
+ NTSTATUS res;
+ IO_STATUS_BLOCK io;
+ FILE_LINK_INFORMATION *pfli;
+ FILE_NAME_INFORMATION *pfni;
+ BOOL success, fileDeleted;
+
+ GetTempPathW( MAX_PATH, tmp_path );
+
+ /* oldpath is a file, newpath doesn't exist */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ DeleteFileW( newpath );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ todo_wine ok( !fileDeleted, "File should exist\n" );
+
+ pfni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
+ res = pNtQueryInformationFile( handle, &io, pfni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
+ ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ pfni->FileName[ pfni->FileNameLength / sizeof(WCHAR) ] = 0;
+ ok( !lstrcmpW(pfni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+ wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(pfni->FileName) );
+ HeapFree( GetProcessHeap(), 0, pfni );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = FALSE, target file opened */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "res expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expect STATUS_SUCCESS, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_SUCCESS, "res expect STATUS_SUCCESS, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE, target file opened */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE, target file opened with FILE_SHARE_DELETE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE, target file opened with FILE_SHARE_DELETE | FILE_SHARE_WRITE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath doesn't exist, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ DeleteFileW( newpath );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef , "io.Status expected 0xdeadbeef, got %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( fileDeleted, "File should have been deleted\n" );
+
+ pfni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
+ res = pNtQueryInformationFile( handle, &io, pfni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
+ ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ pfni->FileName[ pfni->FileNameLength / sizeof(WCHAR) ] = 0;
+ ok( !lstrcmpW(pfni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+ wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(pfni->FileName) );
+ HeapFree( GetProcessHeap(), 0, pfni );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory (but child object opened), newpath doesn't exist, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ lstrcpyW( newpath, oldpath );
+ lstrcatW( newpath, foo_txt );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ DeleteFileW( newpath );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( fileDeleted, "File should not exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a file, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a file, ReplaceIfExists = FALSE, target file opened */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a file, ReplaceIfExists = TRUE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a file, ReplaceIfExists = TRUE, target file opened */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a directory, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ success = CreateDirectoryW( newpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a directory, ReplaceIfExists = TRUE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ success = CreateDirectoryW( newpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a directory, newpath is a directory, ReplaceIfExists = TRUE, target file opened */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( oldpath );
+ success = CreateDirectoryW( oldpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ success = CreateDirectoryW( newpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a directory, ReplaceIfExists = FALSE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ success = CreateDirectoryW( newpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath is a directory, ReplaceIfExists = TRUE */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ success = CreateDirectoryW( newpath, NULL );
+ ok( success != 0, "failed to create temp directory\n" );
+ pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL );
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length );
+ pfli->ReplaceIfExists = TRUE;
+ pfli->RootDirectory = NULL;
+ pfli->FileNameLength = name_str.Length;
+ memcpy( pfli->FileName, name_str.Buffer, name_str.Length );
+ pRtlFreeUnicodeString( &name_str );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+
+ CloseHandle( handle );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+
+ /* oldpath is a file, newpath doesn't exist, test with RootDirectory != NULL */
+ res = GetTempFileNameW( tmp_path, foo, 0, oldpath );
+ ok( res != 0, "fail to create temp file\n" );
+ handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ res = GetTempFileNameW( tmp_path, foo, 0, newpath );
+ ok( res != 0, "fail to create temp file\n" );
+ DeleteFileW( newpath );
+ for (filename = newpath, p = newpath; *p; p++)
+ if (*p == '\\') filename = p + 1;
+ handle2 = CreateFileW( tmp_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+ ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" );
+
+ pfli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + lstrlenW(filename) * sizeof(WCHAR) );
+ pfli->ReplaceIfExists = FALSE;
+ pfli->RootDirectory = handle2;
+ pfli->FileNameLength = lstrlenW(filename) * sizeof(WCHAR);
+ memcpy( pfli->FileName, filename, pfli->FileNameLength );
+
+ U(io).Status = 0xdeadbeef;
+ res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
+ todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
+ todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ ok( !fileDeleted, "File should exist\n" );
+ fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
+ todo_wine ok( !fileDeleted, "File should exist\n" );
+
+ pfni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
+ res = pNtQueryInformationFile( handle, &io, pfni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
+ ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ pfni->FileName[ pfni->FileNameLength / sizeof(WCHAR) ] = 0;
+ ok( !lstrcmpW(pfni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+ wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(pfni->FileName) );
+ HeapFree( GetProcessHeap(), 0, pfni );
+
+ CloseHandle( handle );
+ CloseHandle( handle2 );
+ HeapFree( GetProcessHeap(), 0, pfli );
+ delete_object( oldpath );
+ delete_object( newpath );
+}
+
static void test_file_both_information(void)
{
IO_STATUS_BLOCK io;
@@ -3716,6 +4413,7 @@ START_TEST(file)
test_file_full_size_information();
test_file_all_name_information();
test_file_rename_information();
+ test_file_link_information();
test_file_disposition_information();
test_query_volume_information_file();
test_query_attribute_information_file();
--
2.4.2

View File

@ -0,0 +1,379 @@
From eb0a9708ab107897cb41a20299f530a8d7bddb4c Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 3 Jun 2015 17:04:23 +0200
Subject: server: Implement support for FileLinkInformation class in
NtSetInformationFile.
---
dlls/ntdll/file.c | 42 ++++++++++++++++++++++++++
dlls/ntdll/tests/file.c | 54 ++++++++++++++++-----------------
server/fd.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++-
server/protocol.def | 8 +++++
4 files changed, 155 insertions(+), 28 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 6aa46f4..1e27afb 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -2823,6 +2823,48 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
else io->u.Status = STATUS_INVALID_PARAMETER_3;
break;
+ case FileLinkInformation:
+ if (len >= sizeof(FILE_LINK_INFORMATION))
+ {
+ FILE_LINK_INFORMATION *info = ptr;
+ UNICODE_STRING name_str;
+ OBJECT_ATTRIBUTES attr;
+ ANSI_STRING unix_name;
+
+ name_str.Buffer = info->FileName;
+ name_str.Length = info->FileNameLength;
+ name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR);
+
+ attr.Length = sizeof(attr);
+ attr.ObjectName = &name_str;
+ attr.RootDirectory = info->RootDirectory;
+ attr.Attributes = OBJ_CASE_INSENSITIVE;
+
+ io->u.Status = nt_to_unix_file_name_attr( &attr, &unix_name, FILE_OPEN_IF );
+ if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE)
+ break;
+
+ if (!info->ReplaceIfExists && io->u.Status == STATUS_SUCCESS)
+ {
+ RtlFreeAnsiString( &unix_name );
+ io->u.Status = STATUS_OBJECT_NAME_COLLISION;
+ break;
+ }
+
+ SERVER_START_REQ( link_file )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ req->rootdir = wine_server_obj_handle( attr.RootDirectory );
+ wine_server_add_data( req, unix_name.Buffer, unix_name.Length );
+ io->u.Status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+
+ RtlFreeAnsiString( &unix_name );
+ }
+ else io->u.Status = STATUS_INVALID_PARAMETER_3;
+ break;
+
default:
FIXME("Unsupported class (%d)\n", class);
io->u.Status = STATUS_NOT_IMPLEMENTED;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index c985a03..92613d5 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2183,12 +2183,12 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
- todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
- todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
+ ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
- todo_wine ok( !fileDeleted, "File should exist\n" );
+ ok( !fileDeleted, "File should exist\n" );
pfni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
res = pNtQueryInformationFile( handle, &io, pfni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
@@ -2222,7 +2222,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2255,7 +2255,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "res expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION, "res expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2285,8 +2285,8 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
- todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expect STATUS_SUCCESS, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_SUCCESS, "res expect STATUS_SUCCESS, res is %x\n", res );
+ ok( U(io).Status == STATUS_SUCCESS, "io.Status expect STATUS_SUCCESS, io.Status is %x\n", U(io).Status );
+ ok( res == STATUS_SUCCESS, "res expect STATUS_SUCCESS, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2319,7 +2319,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2353,7 +2353,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2387,7 +2387,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2422,7 +2422,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef , "io.Status expected 0xdeadbeef, got %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2469,7 +2469,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2503,8 +2503,8 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
- "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2540,8 +2540,8 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
- "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2575,7 +2575,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2611,7 +2611,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2648,8 +2648,8 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
- "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION || broken(res == STATUS_FILE_IS_A_DIRECTORY) /* > Win XP */,
+ "expect STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2685,7 +2685,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2724,7 +2724,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
+ ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expect STATUS_FILE_IS_A_DIRECTORY, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2758,7 +2758,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileLinkInformation, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
+ ok( res == STATUS_OBJECT_NAME_COLLISION, "expect STATUS_OBJECT_NAME_COLLISION, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2791,7 +2791,7 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expect 0xdeadbeef, io.Status is %x\n", U(io).Status );
- todo_wine ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
+ ok( res == STATUS_ACCESS_DENIED, "res expect STATUS_ACCESS_DENIED, res is %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
@@ -2824,12 +2824,12 @@ static void test_file_link_information(void)
U(io).Status = 0xdeadbeef;
res = pNtSetInformationFile( handle, &io, pfli, sizeof(FILE_LINK_INFORMATION) + pfli->FileNameLength, FileLinkInformation );
- todo_wine ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
- todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+ ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status );
+ ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
ok( !fileDeleted, "File should exist\n" );
fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND;
- todo_wine ok( !fileDeleted, "File should exist\n" );
+ ok( !fileDeleted, "File should exist\n" );
pfni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
res = pNtQueryInformationFile( handle, &io, pfni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
diff --git a/server/fd.c b/server/fd.c
index 0234fc0..4729170 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -2561,7 +2561,6 @@ DECL_HANDLER(rename_file)
goto out;
}
}
-
if (rename( fd->unix_name, path ))
file_set_error();
else
@@ -2578,3 +2577,81 @@ out:
free( path );
}
+
+/* create hardlink - FIXME: merge with rename_file */
+DECL_HANDLER(link_file)
+{
+ struct fd *fd;
+ char *path;
+
+ if (!(path = mem_alloc( get_req_data_size() + 1 )))
+ return;
+ memcpy( path, get_req_data(), get_req_data_size() );
+ path[ get_req_data_size() ] = 0;
+
+ /* handle rootdir */
+ if (req->rootdir)
+ {
+ char *combined_path = NULL;
+ struct dir *root;
+ if ((root = get_dir_obj( current->process, req->rootdir, 0 )))
+ {
+ struct fd *root_fd;
+ if ((root_fd = get_obj_fd( (struct object *)root )))
+ {
+ combined_path = dup_fd_name( root_fd, path );
+ release_object( root_fd );
+ }
+ release_object( root );
+ }
+
+ if (!combined_path) return;
+ free( path );
+ path = combined_path;
+ }
+
+ /* create a hardlink */
+ if ((fd = get_handle_fd_obj( current->process, req->handle, 0 )))
+ {
+ if (fd->unix_name)
+ {
+ struct stat st;
+ if (fd->unix_fd != -1 && !fstat( fd->unix_fd, &st ) && S_ISDIR( st.st_mode ))
+ {
+ set_error( STATUS_FILE_IS_A_DIRECTORY );
+ goto out;
+ }
+ if (!stat( path, &st ))
+ {
+ struct inode *inode;
+ if (!S_ISREG( st.st_mode ))
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ goto out;
+ }
+ if ((inode = get_inode( st.st_dev, st.st_ino, -1 )))
+ {
+ int is_empty = list_empty( &inode->open );
+ release_object( inode );
+ if (!is_empty)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ goto out;
+ }
+ }
+ if (unlink( path ))
+ {
+ file_set_error();
+ goto out;
+ }
+ }
+ if (link( fd->unix_name, path ))
+ file_set_error();
+ }
+ else set_error( STATUS_OBJECT_TYPE_MISMATCH );
+out:
+ release_object( fd );
+ }
+
+ free( path );
+}
diff --git a/server/protocol.def b/server/protocol.def
index 431d2b9..1b7814b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1215,6 +1215,14 @@ enum server_fd_type
@END
+/* Create a hardlink */
+@REQ(link_file)
+ obj_handle_t handle; /* handle to the file */
+ obj_handle_t rootdir; /* root directory */
+ VARARG(unix_name,string); /* file name */
+@END
+
+
/* Create a socket */
@REQ(create_socket)
unsigned int access; /* wanted access rights */
--
2.4.2

View File

@ -1,3 +1,4 @@
Fixes: [30397] Support for NtSetInformationFile class FileDispositionInformation
Fixes: [30399] Support for NtSetInformationFile class FileRenameInformation
Fixes: Support for NtSetInformationFile class FileLinkInformation
Depends: server-File_Permissions

View File

@ -3025,7 +3025,8 @@ fi
# | * [#30399] Support for NtSetInformationFile class FileRenameInformation
# |
# | Modified files:
# | * dlls/ntdll/file.c, dlls/ntdll/tests/file.c, server/fd.c, server/file.c, server/file.h, server/protocol.def
# | * dlls/ntdll/file.c, dlls/ntdll/tests/file.c, include/winternl.h, server/fd.c, server/file.c, server/file.h,
# | server/protocol.def
# |
if test "$enable_ntdll_FileDispositionInformation" -eq 1; then
patch_apply ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch
@ -3042,6 +3043,9 @@ if test "$enable_ntdll_FileDispositionInformation" -eq 1; then
patch_apply ntdll-FileDispositionInformation/0012-server-When-combining-root-and-name-make-sure-there-.patch
patch_apply ntdll-FileDispositionInformation/0013-server-Reject-rename-when-target-has-opened-file-han.patch
patch_apply ntdll-FileDispositionInformation/0014-server-Manually-unlink-dest-when-trying-to-replace-a.patch
patch_apply ntdll-FileDispositionInformation/0015-include-Add-declaration-for-FILE_LINK_INFORMATION.patch
patch_apply ntdll-FileDispositionInformation/0016-ntdll-tests-Add-tests-for-FileLinkInformation-class.patch
patch_apply ntdll-FileDispositionInformation/0017-server-Implement-support-for-FileLinkInformation-cla.patch
(
echo '+ { "Dmitry Timoshkov", "server: Keep a pointer to parent'\''s fd unix_name in the closed_fd structure.", 1 },';
echo '+ { "Dmitry Timoshkov", "server: Add support for setting file disposition information.", 1 },';
@ -3057,6 +3061,9 @@ if test "$enable_ntdll_FileDispositionInformation" -eq 1; then
echo '+ { "Sebastian Lackner", "server: When combining root and name, make sure there is only one slash.", 2 },';
echo '+ { "Sebastian Lackner", "server: Reject rename when target has opened file handles.", 1 },';
echo '+ { "Sebastian Lackner", "server: Manually unlink dest when trying to replace a file with directory.", 1 },';
echo '+ { "Zhaonan Liang", "include: Add declaration for FILE_LINK_INFORMATION.", 1 },';
echo '+ { "Qian Hong", "ntdll/tests: Add tests for FileLinkInformation class.", 1 },';
echo '+ { "Sebastian Lackner", "server: Implement support for FileLinkInformation class in NtSetInformationFile.", 1 },';
) >> "$patchlist"
fi