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 --- dlls/ntdll/file.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 5175e9d..378584d 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2761,9 +2761,9 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, if (len >= sizeof(FILE_RENAME_INFORMATION)) { FILE_RENAME_INFORMATION *info = ptr; + ANSI_STRING unix_name, src_unix; UNICODE_STRING name_str; OBJECT_ATTRIBUTES attr; - ANSI_STRING unix_name;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -2778,6 +2778,48 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE) break;
+ /* 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. */ + if (io->u.Status != STATUS_NO_SUCH_FILE && !(server_get_unix_name(handle, &src_unix))) + { + if (src_unix.Length == unix_name.Length && + !memcmp(src_unix.Buffer, unix_name.Buffer, unix_name.Length)) + { + size_t nt_filename_len, pathlen; + const WCHAR *nt_filename; + char *tmp; + INT maxsz; + + for (pathlen = name_str.Length / sizeof(WCHAR); pathlen; pathlen--) + if (name_str.Buffer[pathlen - 1] == '\') + break; + + nt_filename = name_str.Buffer + pathlen; + nt_filename_len = name_str.Length / sizeof(WCHAR) - pathlen; + + /* Build it from path + filename (the latter converted from nt_filename) */ + for (pathlen = unix_name.Length; pathlen; pathlen--) + if (unix_name.Buffer[pathlen - 1] == '/') + break; + + tmp = unix_name.Buffer; + maxsz = pathlen + nt_filename_len * 3 + 1; + if (unix_name.MaximumLength < maxsz) + tmp = RtlReAllocateHeap(GetProcessHeap(), 0, tmp, maxsz); + if (tmp) + { + unix_name.Buffer = tmp; + unix_name.MaximumLength = maxsz; + + unix_name.Length = pathlen; + unix_name.Length += ntdll_wcstoumbs(nt_filename, nt_filename_len, + unix_name.Buffer + pathlen, maxsz - pathlen, TRUE); + unix_name.Buffer[unix_name.Length] = '\0'; + } + } + RtlFreeAnsiString(&src_unix); + } + SERVER_START_REQ( set_fd_name_info ) { req->handle = wine_server_obj_handle( handle );
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 785bd60..d0799f4 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1906,6 +1906,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; @@ -1975,9 +1976,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?.*");