On 4/21/21 2:42 PM, Gabriel Ivăncescu wrote:
Renaming a file or directory from e.g. foobar to FooBar (or any other casing change) should work, like on Windows, instead of being a no-op. Clobbering an existing file must also respect the new casing.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46203 Signed-off-by: Gabriel Ivăncescu <gabrielopcode(a)gmail.com> ---
We just prepend the unix casing filename (without path) to the wineserver data before the actual unix filename, and a field `caselen` which is the length of this data.
dlls/ntdll/unix/file.c | 56 ++++++++++++++++++++++-- server/fd.c | 97 ++++++++++++++++++++++++++++-------------- server/protocol.def | 2 + 3 files changed, 118 insertions(+), 37 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 488f748..d8fdb27 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4520,9 +4520,11 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, if (len >= sizeof(FILE_RENAME_INFORMATION)) { FILE_RENAME_INFORMATION *info = ptr; + int nt_filenamelen, casinglen = 0; UNICODE_STRING name_str, redir; + const WCHAR *nt_filename; + char *unix_name, *casing; OBJECT_ATTRIBUTES attr; - char *unix_name;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4530,17 +4532,38 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL ); get_redirect( &attr, &redir );
- io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + nt_filename = attr.ObjectName->Buffer + attr.ObjectName->Length / sizeof(WCHAR); + for (; nt_filename != attr.ObjectName->Buffer; nt_filename--) + if (nt_filename[-1] == '\\') + break; + nt_filenamelen = attr.ObjectName->Buffer + attr.ObjectName->Length / sizeof(WCHAR) - nt_filename; + + if ((casing = malloc( nt_filenamelen * 3 ))) + { + casinglen = ntdll_wcstoumbs( nt_filename, nt_filenamelen, casing, nt_filenamelen * 3, TRUE ); + io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + } + else + io->u.Status = STATUS_NO_MEMORY; + if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE) { + if (casinglen <= 0) + { + casing = strrchr( unix_name, '/' ); + casing = casing ? casing + 1 : unix_name; + casinglen = strlen( casing ); + } SERVER_START_REQ( set_fd_name_info ) { req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; + req->caselen = casinglen; req->link = FALSE; req->replace = info->ReplaceIfExists; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); + wine_server_add_data( req, casing, casinglen ); wine_server_add_data( req, unix_name, strlen(unix_name) ); io->u.Status = wine_server_call( req ); } @@ -4548,6 +4571,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
free( unix_name ); } + free( casing ); free( redir.Buffer ); } else io->u.Status = STATUS_INVALID_PARAMETER_3;
This shouldn't free if the fallback path was taken.
@@ -4557,9 +4581,11 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, if (len >= sizeof(FILE_LINK_INFORMATION)) { FILE_LINK_INFORMATION *info = ptr; + int nt_filenamelen, casinglen = 0; UNICODE_STRING name_str, redir; + const WCHAR *nt_filename; + char *unix_name, *casing; OBJECT_ATTRIBUTES attr; - char *unix_name;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4567,17 +4593,38 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL ); get_redirect( &attr, &redir );
- io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + nt_filename = attr.ObjectName->Buffer + attr.ObjectName->Length / sizeof(WCHAR); + for (; nt_filename != attr.ObjectName->Buffer; nt_filename--) + if (nt_filename[-1] == '\\') + break; + nt_filenamelen = attr.ObjectName->Buffer + attr.ObjectName->Length / sizeof(WCHAR) - nt_filename; + + if ((casing = malloc( nt_filenamelen * 3 ))) + { + casinglen = ntdll_wcstoumbs( nt_filename, nt_filenamelen, casing, nt_filenamelen * 3, TRUE ); + io->u.Status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + } + else + io->u.Status = STATUS_NO_MEMORY; + if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE) { + if (casinglen <= 0) + { + casing = strrchr( unix_name, '/' ); + casing = casing ? casing + 1 : unix_name; + casinglen = strlen( casing ); + } SERVER_START_REQ( set_fd_name_info ) { req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; + req->caselen = casinglen; req->link = TRUE; req->replace = info->ReplaceIfExists; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); + wine_server_add_data( req, casing, casinglen ); wine_server_add_data( req, unix_name, strlen(unix_name) ); io->u.Status = wine_server_call( req ); } @@ -4585,6 +4632,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
free( unix_name ); } + free( casing ); free( redir.Buffer ); } else io->u.Status = STATUS_INVALID_PARAMETER_3;
I'm not sure we want to support a different casing here, I think there are some additional consideration to have on the server side code: We don't want to create link of some file to another casing of itself, but instead probably rename the link to another casing if it already exists? I think we should just use the strrchr fallback all the time here. However, if we want to support proper casing for FileLinkInformation too, it would probably deserve a few tests first to see what this means for links. And the implementation could then come in a different patch to make it clearer. In which case, assuming we need the same kind of logic to send the proper casing, a separate helper would indeed be useful. In any case, I think it would also be nice (if possible?) to have the tests first in the series with todo_wine, removed later in the patch that fixes it. -- Rémi Bernon <rbernon(a)codeweavers.com>