[PATCH v3 1/2] kernel32/path: Allow renaming a file/directory to a different capitalization of itself
Gabriel Ivăncescu
gabrielopcode at gmail.com
Fri Aug 2 06:45:30 CDT 2019
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 at 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)
--
2.21.0
More information about the wine-devel
mailing list