Support rename with SetFileInformationByHandle Implement SetFileInformationByHandle when class=FileRenameInfo. Recent clang-cl.exe has started to use SetFileInformationByHandle to rename a file. info is FILE_RENAME_INFORMATION. FileName contains the path to rename. It might be absolute path, or might be relative path from CWD. Bug: b/67663665 Change-Id: I0ea96b87dff304713344efaa8a66efa0d327b734
diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index d61c56c..cf08634 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c
@@ -1040,10 +1040,121 @@ return FALSE; } +BOOL SetFileInformationByHandle_FileRenameInfo(HANDLE file, + FILE_RENAME_INFORMATION *fri, + DWORD size) { + // forward declaration. + DWORD WINAPI GetFinalPathNameByHandleW(HANDLE file, LPWSTR path, DWORD charcount, DWORD flags); + + BOOL ret = FALSE; + DWORD error = ERROR_INVALID_PARAMETER; + + WCHAR src_path[MAX_PATH + 1], dst_path[MAX_PATH + 1]; + UNICODE_STRING nt_src_path, nt_dst_path; + ANSI_STRING unix_src_path, unix_dst_path; + DWORD len, disposition; + HANDLE hDstFile = INVALID_HANDLE_VALUE; + + nt_src_path.Buffer = NULL; + nt_dst_path.Buffer = NULL; + unix_src_path.Buffer = NULL; + unix_dst_path.Buffer = NULL; + + // Takes the src file information, and create nt_src_path and unix_src_path. + len = GetFinalPathNameByHandleW(file, src_path, MAX_PATH, 0); + if (len == 0 || len > MAX_PATH) { + // When GetFinalPathNameByHandleW failed, it returns 0 or more than MAX_PATH + // when it requires more buffer. + goto done; + } + // src_path will be something like L"\\\\?\\C:\\Temp\\hello-f5751ef4.obj.tmp" + TRACE("(shinyak) src_path=%s\n", debugstr_w(src_path)); + if (!RtlDosPathNameToNtPathName_U(src_path, &nt_src_path, NULL, NULL)) { + TRACE("(shinyak) failed to convert %s to ntpath", debugstr_w(src_path)); + goto done; + } + if (wine_nt_to_unix_file_name(&nt_src_path, &unix_src_path, FILE_OPEN, FALSE) != STATUS_SUCCESS) { + TRACE("(shinyak) failed to convert src_path=%s\n", debugstr_w(src_path)); + goto done; + } + + // Open dst file. If the dst file exists, we're able to take unix name. + // If `Replace` is false and the same name file exists, CreateFileW will fail. + disposition = fri->Replace ? CREATE_ALWAYS : CREATE_NEW; + hDstFile = CreateFileW(fri->FileName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + disposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hDstFile == INVALID_HANDLE_VALUE) { + TRACE("(shinyak) failed to open %s\n", fri->FileName); + goto done; + } + + len = GetFinalPathNameByHandleW(hDstFile, dst_path, MAX_PATH, 0); + if (len == 0 || len > MAX_PATH) { + goto done; + } + TRACE("(shinyak) dst_path=%s\n", debugstr_w(dst_path)); + if (!RtlDosPathNameToNtPathName_U(dst_path, &nt_dst_path, NULL, NULL)) { + TRACE("(shinyak) failed to convert %s to ntpath", debugstr_w(dst_path)); + goto done; + } + if (wine_nt_to_unix_file_name(&nt_dst_path, &unix_dst_path, FILE_OPEN, FALSE) != STATUS_SUCCESS) { + TRACE("(shinyak) failed to convert src_path=%s\n", debugstr_w(dst_path)); + goto done; + } + + // Now, we have unix filename for src and dst. + // Call `rename` like ReplaceFileW. + if (rename(unix_src_path.Buffer, unix_dst_path.Buffer) < 0) { + error = ERROR_UNABLE_TO_REMOVE_REPLACED; // Is this correct? + goto done; + } + + ret = TRUE; + +done: + RtlFreeUnicodeString(&nt_src_path); + RtlFreeUnicodeString(&nt_dst_path); + RtlFreeAnsiString(&unix_src_path); + RtlFreeAnsiString(&unix_dst_path); + + if (hDstFile != INVALID_HANDLE_VALUE) + CloseHandle(hDstFile); + if (!ret) + SetLastError(error); + return ret; +} + BOOL WINAPI SetFileInformationByHandle( HANDLE file, FILE_INFO_BY_HANDLE_CLASS class, VOID *info, DWORD size ) { - FIXME("%p %u %p %u - stub\n", file, class, info, size); - return FALSE; + if (class == FileRenameInfo) { + // The actual content of info. (xx) is added by me. + // ( 8) 01 00 00 00 00 00 00 00 + // ( 8) 00 00 00 00 00 00 00 00 + // ( 4) 09 00 00 00 + // (20) 68 00 65 00 6C 00 6C 00 6F 00 2E 00 6F 00 62 00 6A 00 00 00 + // ( h e l l o . o b j \0 ) + // + // This is FILE_RENAME_INFORMATION described in winternl.h. + // The actual info seems like the following + // ReplaceIfExists: 8 byte (BOOLEAN 1 byte + 7byte padding) + // RootDirectory: 8 byte (HANDLE) + // FileNameLength: 4 byte (ULONG) + // FileName: WCHAR array + // and this is different from FILE_RENAME_INFO described in + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364398(v=vs.85).aspx + // + + FILE_RENAME_INFORMATION *fri = (FILE_RENAME_INFORMATION *)(info); + return SetFileInformationByHandle_FileRenameInfo(file, fri, size); + } + + FIXME("%p %u %p %u - stub\n", file, class, info, size); + return FALSE; } /***********************************************************************