Renaming a file or directory from e.g. foobar to FooBar (or any other caps-only change) should work and capitalize it, like on Windows, instead of being a no-op.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46203 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v3: Divide nt_name.Length by sizeof(WCHAR) since it represents the number of bytes, not wchars.
dlls/kernel32/path.c | 73 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 5fed28d..bdb2e80 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -1136,6 +1136,16 @@ static BOOL is_same_file(HANDLE h1, HANDLE h2) return ret; }
+static ULONG get_handle_attr(HANDLE handle) +{ + FILE_BASIC_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + + status = NtQueryInformationFile(handle, &io, &info, sizeof(info), FileBasicInformation); + return status != STATUS_SUCCESS ? 0 : info.FileAttributes; +} + /************************************************************************** * CopyFileW (KERNEL32.@) */ @@ -1317,6 +1327,7 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, NTSTATUS status; HANDLE source_handle = 0, dest_handle = 0; ANSI_STRING source_unix, dest_unix; + BOOL same_file = FALSE; DWORD options;
TRACE("(%s,%s,%p,%p,%04x)\n", @@ -1369,18 +1380,22 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, SetLastError( ERROR_PATH_NOT_FOUND ); goto error; } - options = FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT; + options = FILE_SYNCHRONOUS_IO_NONALERT; if (flag & MOVEFILE_WRITE_THROUGH) options |= FILE_WRITE_THROUGH; status = NtOpenFile( &dest_handle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, options ); if (status == STATUS_SUCCESS) /* destination exists */ { - if (!(flag & MOVEFILE_REPLACE_EXISTING)) + ULONG attrib = 0; + + if (!(flag & MOVEFILE_REPLACE_EXISTING) || + ((attrib = get_handle_attr(dest_handle)) & FILE_ATTRIBUTE_DIRECTORY)) { - if (!is_same_file( source_handle, dest_handle )) + if (!(same_file = is_same_file( source_handle, dest_handle ))) { - SetLastError( ERROR_ALREADY_EXISTS ); + SetLastError( (attrib & FILE_ATTRIBUTE_DIRECTORY) + ? ERROR_ACCESS_DENIED : ERROR_ALREADY_EXISTS ); RtlFreeUnicodeString( &nt_name ); goto error; } @@ -1390,6 +1405,7 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, SetLastError( ERROR_ACCESS_DENIED ); goto error; } + else same_file = is_same_file( source_handle, dest_handle );
NtClose( dest_handle ); dest_handle = NULL; @@ -1402,13 +1418,60 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, }
status = wine_nt_to_unix_file_name( &nt_name, &dest_unix, FILE_OPEN_IF, FALSE ); - RtlFreeUnicodeString( &nt_name ); if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE) { + RtlFreeUnicodeString( &nt_name ); SetLastError( RtlNtStatusToDosError(status) ); goto error; }
+ /* When it's renaming to the same file, preserve the case sensitivity of the last + component, so that renaming a\b\c\foobar to a\b\c\Foobar works correctly like + on Windows, but note that due to hard links the paths must be exactly identical */ + if (same_file && source_unix.Length == dest_unix.Length && + !memcmp(source_unix.Buffer, dest_unix.Buffer, dest_unix.Length)) + { + size_t nt_filename_len, pathlen; + const WCHAR *nt_filename; + BOOL used_default; + char *tmp; + INT num; + + nt_filename = memrchrW(nt_name.Buffer, '\', nt_name.Length / sizeof(WCHAR)) + 1; + nt_filename_len = nt_name.Length / sizeof(WCHAR) - (nt_filename - nt_name.Buffer); + + /* Build it from path + filename (the latter converted from nt_name) */ + num = WideCharToMultiByte(CP_UNIXCP, 0, nt_filename, nt_filename_len, + NULL, 0, NULL, &used_default); + if (!num && GetLastError() == ERROR_INVALID_PARAMETER) + { + num = WideCharToMultiByte(CP_UNIXCP, 0, nt_filename, nt_filename_len, + NULL, 0, NULL, NULL); + used_default = FALSE; + } + + if (!used_default && num > 0) + { + for (pathlen = dest_unix.Length; --pathlen > 0;) + if (dest_unix.Buffer[pathlen] == '/') { pathlen++; break; } + + tmp = dest_unix.Buffer; + if (dest_unix.MaximumLength < pathlen + num + 1) + tmp = RtlReAllocateHeap(GetProcessHeap(), 0, tmp, pathlen + num + 1); + if (tmp) + { + dest_unix.Buffer = tmp; + dest_unix.Length = pathlen + num; + dest_unix.MaximumLength = dest_unix.Length + 1; + dest_unix.Buffer[dest_unix.Length] = '\0'; + + WideCharToMultiByte(CP_UNIXCP, 0, nt_filename, nt_filename_len, + dest_unix.Buffer + pathlen, num, NULL, NULL); + } + } + } + RtlFreeUnicodeString( &nt_name ); + /* now perform the rename */
if (rename( source_unix.Buffer, dest_unix.Buffer ) == -1)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernel32/tests/file.c | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 63a67ca..bdcfb7a 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1897,6 +1897,7 @@ static void test_MoveFileA(void) char tempdir[MAX_PATH]; char source[MAX_PATH], dest[MAX_PATH]; static const char prefix[] = "pfx"; + WIN32_FIND_DATAA find_data; HANDLE hfile; HANDLE hmapfile; DWORD ret; @@ -1966,9 +1967,48 @@ static void test_MoveFileA(void) ok(ret, "MoveFileA: failed, error %d\n", GetLastError());
lstrcatA(tempdir, "Remove Me"); + + /* test renaming a file "Remove Me" to itself but in lowercase "me" */ + lstrcpyA(source, tempdir); + tempdir[lstrlenA(tempdir) - 2] = 'm'; + + hfile = CreateFileA(source, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE, "failed to create %s\n", source); + CloseHandle(hfile); + + ret = MoveFileA(source, tempdir); + ok(ret, "MoveFileA: failed, error %d\n", GetLastError()); + + hfile = FindFirstFileA(tempdir, &find_data); + ok(hfile != INVALID_HANDLE_VALUE, "FindFirstFileA: failed, error %d\n", GetLastError()); + if (hfile != INVALID_HANDLE_VALUE) + { + ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + "MoveFile failed to change caps on same file: got %s\n", find_data.cFileName); + } + CloseHandle(hfile); + + ret = DeleteFileA(tempdir); + ok(ret, "DeleteFileA: error %d\n", GetLastError()); + + /* now test a directory from "Remove me" to uppercase "Me" */ ret = CreateDirectoryA(tempdir, NULL); ok(ret == TRUE, "CreateDirectoryA failed\n");
+ lstrcpyA(source, tempdir); + tempdir[lstrlenA(tempdir) - 2] = 'M'; + ret = MoveFileA(source, tempdir); + ok(ret, "MoveFileA: failed, error %d\n", GetLastError()); + + hfile = FindFirstFileA(tempdir, &find_data); + ok(hfile != INVALID_HANDLE_VALUE, "FindFirstFileA: failed, error %d\n", GetLastError()); + if (hfile != INVALID_HANDLE_VALUE) + { + ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + "MoveFile failed to change caps on same directory: got %s\n", find_data.cFileName); + } + CloseHandle(hfile); + lstrcpyA(source, dest); lstrcpyA(dest, tempdir); lstrcatA(dest, "\wild?.*");
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=55106
Your paranoid android.
=== debian10 (32 bit report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit Chinese:China report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (64 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=55105
Your paranoid android.
=== debian10 (32 bit report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit Chinese:China report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (64 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
These are likely false positives or errors from other code. Since I couldn't reproduce it on my machine, I added ERR traces to see if it fails somewhere in the patch's code for the testbot, and this time there were no errors at all (even though the code was the same, but with added ERR traces).
Please see: https://testbot.winehq.org/JobDetails.pl?Key=55110
On 8/2/19 2:54 PM, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=55105
Your paranoid android.
=== debian10 (32 bit report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit Chinese:China report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (32 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)
=== debian10 (64 bit WoW report) ===
kernel32: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x00438134).
Report errors: kernel32:virtual prints too much data (91050 bytes)