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;
}
/***********************************************************************