FileRenameInfoEx is already handled by NtSetInformationFile, SetFileInformationByHandle need only pass it through correctly.
As of version 5.3, Apple's Metal Developer Tools are dependent on SetFileInformationByHandle to handle FileRenameInfoEx in this fashion.
-- v2: kernelbase: pass FileRenameInfoEx to NtSetInformationFile kernel32/tests: SetFileInfo should accept FileRenameInfoEx
From: Michael Stopa michael.stopa@tensorworks.com.au
The documentation is extremely sparse but it would appear that SetFileInformationByHandle should handle FileRenameInfoEx in basically the same manner as FileRenameInfo, the only difference between the two being how they store the flag for replacing an existing file.
No other flags are being tested as even on MSDN they're only properly explained for NtSetInformationFile, which is wrapped by SetFileInformationByHandle in Wine anyway. --- dlls/kernel32/tests/file.c | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index db3ffc655ce..dcd90803bb1 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -5605,6 +5605,73 @@ static void test_SetFileRenameInfo(void) DeleteFileW(tempFileTo2); }
+static void test_SetFileRenameInfoEx(void) +{ + WCHAR tempFileFrom[MAX_PATH], tempFileTo1[MAX_PATH], tempFileTo2[MAX_PATH]; + WCHAR tempPath[MAX_PATH]; + FILE_RENAME_INFORMATION *fri; + HANDLE file; + DWORD size; + BOOL ret; + + if (!pSetFileInformationByHandle) + { + win_skip("SetFileInformationByHandle is not supported\n"); + return; + } + + ret = GetTempPathW(MAX_PATH, tempPath); + ok(ret, "GetTempPathW failed, got error %lu.\n", GetLastError()); + + ret = GetTempFileNameW(tempPath, L"abc", 0, tempFileFrom); + ok(ret, "GetTempFileNameW failed, got error %lu.\n", GetLastError()); + + ret = GetTempFileNameW(tempPath, L"abc", 0, tempFileTo1); + ok(ret, "GetTempFileNameW failed, got error %lu.\n", GetLastError()); + + ret = GetTempFileNameW(tempPath, L"abc", 1, tempFileTo2); + ok(ret, "GetTempFileNameW failed, got error %lu.\n", GetLastError()); + + file = CreateFileW(tempFileFrom, GENERIC_READ | GENERIC_WRITE | DELETE, 0, 0, OPEN_EXISTING, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "failed to create temp file, error %lu.\n", GetLastError()); + + size = sizeof(FILE_RENAME_INFORMATION) + MAX_PATH; + fri = HeapAlloc(GetProcessHeap(), 0, size); + + fri->Flags = 0; + fri->RootDirectory = NULL; + fri->FileNameLength = wcslen(tempFileTo1) * sizeof(WCHAR); + memcpy(fri->FileName, tempFileTo1, fri->FileNameLength + sizeof(WCHAR)); + ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); + todo_wine ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS, "FileRenameInfoEx unexpected result %ld\n", GetLastError()); + + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; + ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); + todo_wine ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError()); + + fri->Flags = 0; + fri->FileNameLength = wcslen(tempFileTo2) * sizeof(WCHAR); + memcpy(fri->FileName, tempFileTo2, fri->FileNameLength + sizeof(WCHAR)); + ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); + todo_wine ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError()); + CloseHandle(file); + + file = CreateFileW(tempFileTo2, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + todo_wine ok(file != INVALID_HANDLE_VALUE, "file not renamed, error %ld\n", GetLastError()); + + fri->FileNameLength = wcslen(tempFileTo1) * sizeof(WCHAR); + memcpy(fri->FileName, tempFileTo1, fri->FileNameLength + sizeof(WCHAR)); + ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); + todo_wine + ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "FileRenameInfoEx unexpected result %ld\n", GetLastError()); + CloseHandle(file); + + HeapFree(GetProcessHeap(), 0, fri); + DeleteFileW(tempFileFrom); + DeleteFileW(tempFileTo1); + DeleteFileW(tempFileTo2); +} + static void test_GetFileAttributesExW(void) { static const struct @@ -6385,6 +6452,7 @@ START_TEST(file) test_GetFinalPathNameByHandleW(); test_SetFileInformationByHandle(); test_SetFileRenameInfo(); + test_SetFileRenameInfoEx(); test_GetFileAttributesExW(); test_post_completion(); test_overlapped_read();
From: Michael Stopa michael.stopa@tensorworks.com.au
FileRenameInfoEx is already handled by NtSetInformationFile, SetFileInformationByHandle need only pass it through. --- dlls/kernel32/tests/file.c | 8 ++++---- dlls/kernelbase/file.c | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index dcd90803bb1..1809e141d9b 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -5643,21 +5643,21 @@ static void test_SetFileRenameInfoEx(void) fri->FileNameLength = wcslen(tempFileTo1) * sizeof(WCHAR); memcpy(fri->FileName, tempFileTo1, fri->FileNameLength + sizeof(WCHAR)); ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); - todo_wine ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS, "FileRenameInfoEx unexpected result %ld\n", GetLastError()); + ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS, "FileRenameInfoEx unexpected result %ld\n", GetLastError());
fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); - todo_wine ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError()); + ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError());
fri->Flags = 0; fri->FileNameLength = wcslen(tempFileTo2) * sizeof(WCHAR); memcpy(fri->FileName, tempFileTo2, fri->FileNameLength + sizeof(WCHAR)); ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size); - todo_wine ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError()); + ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError()); CloseHandle(file);
file = CreateFileW(tempFileTo2, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - todo_wine ok(file != INVALID_HANDLE_VALUE, "file not renamed, error %ld\n", GetLastError()); + ok(file != INVALID_HANDLE_VALUE, "file not renamed, error %ld\n", GetLastError());
fri->FileNameLength = wcslen(tempFileTo1) * sizeof(WCHAR); memcpy(fri->FileName, tempFileTo1, fri->FileNameLength + sizeof(WCHAR)); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 7d8b6844456..042179a8378 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -3658,6 +3658,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetFileInformationByHandle( HANDLE file, FILE_INFO status = NtSetInformationFile( file, &io, info, size, FileIoPriorityHintInformation ); break; case FileRenameInfo: + case FileRenameInfoEx: { FILE_RENAME_INFORMATION *rename_info; UNICODE_STRING nt_name; @@ -3673,7 +3674,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetFileInformationByHandle( HANDLE file, FILE_INFO memcpy( rename_info, info, sizeof(*rename_info) ); memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length + sizeof(WCHAR) ); rename_info->FileNameLength = nt_name.Length; - status = NtSetInformationFile( file, &io, rename_info, size, FileRenameInformation ); + status = NtSetInformationFile( file, &io, rename_info, size, + class == FileRenameInfo ? FileRenameInformation : FileRenameInformationEx ); HeapFree( GetProcessHeap(), 0, rename_info ); } RtlFreeUnicodeString( &nt_name );
On Fri Jun 13 05:27:09 2025 +0000, Michael Stopa wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/8293/diffs?diff_id=185231&start_sha=161a1ff40add18d073d8832d1effe9a1cfd62e92#8d569ecd47342a97f565014a9b59887f2151a473_62_49)
That's because it's a typo, it was supposed to be checking for `pSetFileInformationByHandle`
On Fri Jun 13 05:27:09 2025 +0000, Michael Stopa wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/8293/diffs?diff_id=185231&start_sha=161a1ff40add18d073d8832d1effe9a1cfd62e92#8d569ecd47342a97f565014a9b59887f2151a473_81_49)
I was only intending to check if FileRenameInfoEx was being handled at all, there's a more comprehensive test in `kernel32/tests` for the regular `FileRenameInfo` case so I've removed this one and replaced it with that.
On Fri Jun 13 05:27:09 2025 +0000, Michael Stopa wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/8293/diffs?diff_id=185231&start_sha=161a1ff40add18d073d8832d1effe9a1cfd62e92#8d569ecd47342a97f565014a9b59887f2151a473_95_49)
That is what `FILE_FLAG_DELETE_ON_CLOSE` is supposed to handle in `CreateFileW` further up
Alexandre Julliard (@julliard) commented about dlls/kernel32/tests/file.c:
- fri->Flags = 0;
- fri->FileNameLength = wcslen(tempFileTo2) * sizeof(WCHAR);
- memcpy(fri->FileName, tempFileTo2, fri->FileNameLength + sizeof(WCHAR));
- ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size);
- ok(ret, "FileRenameInfoEx failed, error %ld\n", GetLastError());
- CloseHandle(file);
- file = CreateFileW(tempFileTo2, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
- ok(file != INVALID_HANDLE_VALUE, "file not renamed, error %ld\n", GetLastError());
- fri->FileNameLength = wcslen(tempFileTo1) * sizeof(WCHAR);
- memcpy(fri->FileName, tempFileTo1, fri->FileNameLength + sizeof(WCHAR));
- ret = pSetFileInformationByHandle(file, FileRenameInfoEx, fri, size);
- todo_wine
- ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "FileRenameInfoEx unexpected result %ld\n", GetLastError());
- CloseHandle(file);
Please merge that into the existing FileRenameInfo tests, there's no need to duplicate the whole thing.