Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernel32/tests/file.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 5deed96..bb05188 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1954,6 +1954,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; @@ -2023,9 +2024,72 @@ 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) + { + todo_wine ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + "MoveFile failed to change casing on same file: got %s\n", find_data.cFileName); + } + CloseHandle(hfile); + + /* test renaming another file "Remove Be" to "Remove Me", which replaces the existing "Remove me" */ + tempdir[lstrlenA(tempdir) - 2] = 'B'; + + hfile = CreateFileA(tempdir, 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", tempdir); + CloseHandle(hfile); + + ret = MoveFileA(tempdir, source); + ok(!ret, "MoveFileA: expected failure\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "MoveFileA: expected ERROR_ALREADY_EXISTS, got %d\n", GetLastError()); + ret = MoveFileExA(tempdir, source, MOVEFILE_REPLACE_EXISTING); + ok(ret, "MoveFileExA: failed, error %d\n", GetLastError()); + + tempdir[lstrlenA(tempdir) - 2] = 'm'; + + hfile = FindFirstFileA(tempdir, &find_data); + ok(hfile != INVALID_HANDLE_VALUE, "FindFirstFileA: failed, error %d\n", GetLastError()); + if (hfile != INVALID_HANDLE_VALUE) + { + ok(!lstrcmpA(strrchr(source, '\') + 1, find_data.cFileName), + "MoveFile failed to change casing on existing target 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) + { + todo_wine ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + "MoveFile failed to change casing on same directory: got %s\n", find_data.cFileName); + } + CloseHandle(hfile); + lstrcpyA(source, dest); lstrcpyA(dest, tempdir); lstrcatA(dest, "\wild?.*");
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernel32/tests/file.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index bb05188..8560524 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -5790,6 +5790,11 @@ static void test_hard_link(void) ok(!ret, "expected failure\n"); ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError());
+ SetLastError(0xdeadbeef); + ret = CreateHardLinkA( "WineTest_File1", "winetest_file1", NULL ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError()); + SetLastError(0xdeadbeef); ret = CreateHardLinkA( "winetest_file3", "winetest_dir1", NULL ); ok(!ret, "expected failure\n");
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/ntdll/tests/file.c | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 839046a..d469b44 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2176,6 +2176,7 @@ static void test_file_link_information(void) WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16], *filename, *p; FILE_LINK_INFORMATION *fli; FILE_NAME_INFORMATION *fni; + WIN32_FIND_DATAW find_data; BOOL success, fileDeleted; UNICODE_STRING name_str; HANDLE handle, handle2; @@ -2283,6 +2284,46 @@ static void test_file_link_information(void) delete_object( oldpath ); delete_object( newpath );
+ /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE, different casing on link */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + wcsrchr( newpath, '\' )[1] = 'F'; + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->ReplaceIfExists = TRUE; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + U(io).Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); + ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + handle = FindFirstFileW( newpath, &find_data ); + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %d\n", GetLastError()); + if (handle != INVALID_HANDLE_VALUE) + { + todo_wine ok(!lstrcmpW(wcsrchr(newpath, '\') + 1, find_data.cFileName), + "Link did not change casing on existing target file: got %s\n", wine_dbgstr_w(find_data.cFileName)); + } + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + /* oldpath is a file, newpath is a file, ReplaceIfExists = FALSE, target file opened */ res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); ok( res != 0, "failed to create temp file\n" ); @@ -2826,6 +2867,46 @@ static void test_file_link_information(void) CloseHandle( handle ); HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); + + /* oldpath == newpath, different casing on link */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + wcsrchr( oldpath, '\' )[1] = 'F'; + pRtlDosPathNameToNtPathName_U( oldpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fli->ReplaceIfExists = FALSE; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + U(io).Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); + todo_wine ok( U(io).Status == 0xdeadbeef, "got io status %#x\n", U(io).Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "got status %x\n", res ); + + fli->ReplaceIfExists = TRUE; + U(io).Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); + ok( U(io).Status == STATUS_SUCCESS, "got io status %#x\n", U(io).Status ); + ok( res == STATUS_SUCCESS, "got status %x\n", res ); + ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" ); + + CloseHandle( handle ); + handle = FindFirstFileW( oldpath, &find_data ); + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %d\n", GetLastError()); + if (handle != INVALID_HANDLE_VALUE) + { + todo_wine ok(!lstrcmpW(wcsrchr(oldpath, '\') + 1, find_data.cFileName), + "Link did not change casing on same file: got %s\n", wine_dbgstr_w(find_data.cFileName)); + } + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); }
static void test_file_both_information(void)
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@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/kernel32/tests/file.c | 4 +- dlls/ntdll/tests/file.c | 4 +- dlls/ntdll/unix/file.c | 58 ++++++++++++++++++++-- server/fd.c | 98 +++++++++++++++++++++++++------------- server/protocol.def | 2 + 5 files changed, 126 insertions(+), 40 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 8560524..9ca56e1 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2040,7 +2040,7 @@ static void test_MoveFileA(void) ok(hfile != INVALID_HANDLE_VALUE, "FindFirstFileA: failed, error %d\n", GetLastError()); if (hfile != INVALID_HANDLE_VALUE) { - todo_wine ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), "MoveFile failed to change casing on same file: got %s\n", find_data.cFileName); } CloseHandle(hfile); @@ -2085,7 +2085,7 @@ static void test_MoveFileA(void) ok(hfile != INVALID_HANDLE_VALUE, "FindFirstFileA: failed, error %d\n", GetLastError()); if (hfile != INVALID_HANDLE_VALUE) { - todo_wine ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), + ok(!lstrcmpA(strrchr(tempdir, '\') + 1, find_data.cFileName), "MoveFile failed to change casing on same directory: got %s\n", find_data.cFileName); } CloseHandle(hfile); diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index d469b44..49f4e34 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2315,7 +2315,7 @@ static void test_file_link_information(void) ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %d\n", GetLastError()); if (handle != INVALID_HANDLE_VALUE) { - todo_wine ok(!lstrcmpW(wcsrchr(newpath, '\') + 1, find_data.cFileName), + ok(!lstrcmpW(wcsrchr(newpath, '\') + 1, find_data.cFileName), "Link did not change casing on existing target file: got %s\n", wine_dbgstr_w(find_data.cFileName)); }
@@ -2900,7 +2900,7 @@ static void test_file_link_information(void) ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %d\n", GetLastError()); if (handle != INVALID_HANDLE_VALUE) { - todo_wine ok(!lstrcmpW(wcsrchr(oldpath, '\') + 1, find_data.cFileName), + ok(!lstrcmpW(wcsrchr(oldpath, '\') + 1, find_data.cFileName), "Link did not change casing on same file: got %s\n", wine_dbgstr_w(find_data.cFileName)); }
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index e32de57..908beb6 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -3400,6 +3400,50 @@ NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, U }
+/*********************************************************************** + * nt_to_unix_file_name_with_casing + * + * Same as nt_to_unix_file_name, but additionally return unix file name + * without path, with the original casing from the NT file name. + */ +static NTSTATUS nt_to_unix_file_name_with_casing( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char **casing_ret, UINT disposition ) +{ + int len = attr->ObjectName->Length / sizeof(WCHAR); + const WCHAR *nt_filename; + NTSTATUS status; + char *casing; + + /* NT name may not be NUL terminated; look for last \ character */ + for (nt_filename = attr->ObjectName->Buffer + len; nt_filename != attr->ObjectName->Buffer; nt_filename--) + if (nt_filename[-1] == '\') + break; + len = attr->ObjectName->Buffer + len - nt_filename; + + if (!(casing = malloc( len * 3 + 1 ))) return STATUS_NO_MEMORY; + + status = nt_to_unix_file_name( attr, name_ret, disposition ); + if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE) + { + free( casing ); + return status; + } + + len = ntdll_wcstoumbs( nt_filename, len, casing, len * 3, TRUE ); + if (len > 0) + casing[len] = 0; + else + { + char *p = strrchr( *name_ret, '/' ); + p = p ? p + 1 : *name_ret; + strcpy( casing, p ); + } + + *casing_ret = casing; + return status; +} + + /****************************************************************************** * wine_nt_to_unix_file_name * @@ -4522,8 +4566,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, { FILE_RENAME_INFORMATION *info = ptr; UNICODE_STRING name_str, redir; + char *unix_name, *casing; OBJECT_ATTRIBUTES attr; - char *unix_name;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4531,7 +4575,7 @@ 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 ); + io->u.Status = nt_to_unix_file_name_with_casing( &attr, &unix_name, &casing, FILE_OPEN_IF ); if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE) { SERVER_START_REQ( set_fd_name_info ) @@ -4539,15 +4583,18 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; + req->caselen = strlen( casing ); req->link = FALSE; req->replace = info->ReplaceIfExists; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); + wine_server_add_data( req, casing, req->caselen ); wine_server_add_data( req, unix_name, strlen(unix_name) ); io->u.Status = wine_server_call( req ); } SERVER_END_REQ;
free( unix_name ); + free( casing ); } free( redir.Buffer ); } @@ -4559,8 +4606,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, { FILE_LINK_INFORMATION *info = ptr; UNICODE_STRING name_str, redir; + char *unix_name, *casing; OBJECT_ATTRIBUTES attr; - char *unix_name;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4568,7 +4615,7 @@ 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 ); + io->u.Status = nt_to_unix_file_name_with_casing( &attr, &unix_name, &casing, FILE_OPEN_IF ); if (io->u.Status == STATUS_SUCCESS || io->u.Status == STATUS_NO_SUCH_FILE) { SERVER_START_REQ( set_fd_name_info ) @@ -4576,15 +4623,18 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; + req->caselen = strlen( casing ); req->link = TRUE; req->replace = info->ReplaceIfExists; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); + wine_server_add_data( req, casing, req->caselen ); wine_server_add_data( req, unix_name, strlen(unix_name) ); io->u.Status = wine_server_call( req ); } SERVER_END_REQ;
free( unix_name ); + free( casing ); } free( redir.Buffer ); } diff --git a/server/fd.c b/server/fd.c index 481e9a8..0f7067d 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2488,11 +2488,14 @@ static void set_fd_disposition( struct fd *fd, int unlink )
/* set new name for the fd */ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, data_size_t len, - struct unicode_str nt_name, int create_link, int replace ) + const char *casing, data_size_t casinglen, struct unicode_str nt_name, + int create_link, int replace ) { + size_t pathlen, filenamelen; + int different_casing; struct inode *inode; struct stat st, st2; - char *name; + char *name, *tmp;
if (!fd->inode || !fd->unix_name) { @@ -2526,6 +2529,23 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da name = combined_name; }
+ tmp = strrchr( name, '/' ); + tmp = tmp ? tmp + 1 : name; + pathlen = tmp - name; + filenamelen = strlen( tmp ); + different_casing = (filenamelen != casinglen || memcmp( tmp, casing, casinglen )); + + if (filenamelen < casinglen) + { + tmp = realloc( name, pathlen + casinglen + 1 ); + if (!tmp) + { + set_error( STATUS_NO_MEMORY ); + goto failed; + } + name = tmp; + } + /* when creating a hard link, source cannot be a dir */ if (create_link && !fstat( fd->unix_fd, &st ) && S_ISDIR( st.st_mode )) { @@ -2538,47 +2558,58 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da if (!fstat( fd->unix_fd, &st2 ) && st.st_ino == st2.st_ino && st.st_dev == st2.st_dev) { if (create_link && !replace) set_error( STATUS_OBJECT_NAME_COLLISION ); - free( name ); - return; - } + if (!different_casing) + { + free( name ); + return; + }
- if (!replace) - { - set_error( STATUS_OBJECT_NAME_COLLISION ); - goto failed; + /* creating a link with a different casing on itself renames the file */ + create_link = 0; } - - /* can't replace directories or special files */ - if (!S_ISREG( st.st_mode )) + else { - set_error( STATUS_ACCESS_DENIED ); - goto failed; - } + if (!replace) + { + set_error( STATUS_OBJECT_NAME_COLLISION ); + goto failed; + }
- /* can't replace an opened file */ - if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) - { - int is_empty = list_empty( &inode->open ); - release_object( inode ); - if (!is_empty) + /* can't replace directories or special files */ + if (!S_ISREG( st.st_mode )) { set_error( STATUS_ACCESS_DENIED ); goto failed; } - }
- /* link() expects that the target doesn't exist */ - /* rename() cannot replace files with directories */ - if (create_link || S_ISDIR( st2.st_mode )) - { - if (unlink( name )) + /* can't replace an opened file */ + if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) { - file_set_error(); - goto failed; + int is_empty = list_empty( &inode->open ); + release_object( inode ); + if (!is_empty) + { + set_error( STATUS_ACCESS_DENIED ); + goto failed; + } + } + + /* link() expects that the target doesn't exist */ + /* rename() cannot replace files with directories */ + if (create_link || S_ISDIR( st2.st_mode ) || different_casing) + { + if (unlink( name )) + { + file_set_error(); + goto failed; + } } } }
+ memcpy( name + pathlen, casing, casinglen ); + name[pathlen + casinglen] = 0; + if (create_link) { if (link( fd->unix_name, name )) @@ -2879,16 +2910,19 @@ DECL_HANDLER(set_fd_disp_info) /* set fd name information */ DECL_HANDLER(set_fd_name_info) { + const char *fullname, *casing; struct fd *fd, *root_fd = NULL; struct unicode_str nt_name;
- if (req->namelen > get_req_data_size()) + if (req->namelen > get_req_data_size() || get_req_data_size() - req->namelen < req->caselen) { set_error( STATUS_INVALID_PARAMETER ); return; } nt_name.str = get_req_data(); nt_name.len = (req->namelen / sizeof(WCHAR)) * sizeof(WCHAR); + casing = (const char *)get_req_data() + req->namelen; + fullname = casing + req->caselen;
if (req->rootdir) { @@ -2902,8 +2936,8 @@ DECL_HANDLER(set_fd_name_info)
if ((fd = get_handle_fd_obj( current->process, req->handle, 0 ))) { - set_fd_name( fd, root_fd, (const char *)get_req_data() + req->namelen, - get_req_data_size() - req->namelen, nt_name, req->link, req->replace ); + set_fd_name( fd, root_fd, fullname, (const char *)get_req_data() + get_req_data_size() - fullname, + casing, req->caselen, nt_name, req->link, req->replace ); release_object( fd ); } if (root_fd) release_object( root_fd ); diff --git a/server/protocol.def b/server/protocol.def index 9ea6967..02e5ab0 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3531,9 +3531,11 @@ struct handle_info obj_handle_t handle; /* handle to a file or directory */ obj_handle_t rootdir; /* root directory */ data_size_t namelen; /* length of NT name in bytes */ + data_size_t caselen; /* length of the casing filename */ int link; /* link instead of renaming */ int replace; /* replace an existing file? */ VARARG(name,unicode_str,namelen); /* NT name */ + VARARG(casing,string,caselen);/* new file name's actual casing (without path) */ VARARG(filename,string); /* new file name */ @END
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=89261
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
ntdll: om.c:2320: Test failed: got 88 om.c:2320: Test failed: got 88
=== debiant2 (32 bit WoW report) ===
ntdll: om.c:2320: Test failed: got 89
=== debiant2 (64 bit WoW report) ===
ntdll: om.c:2320: Test failed: got 89
On 4/23/21 5:52 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
nt_to_unix_file_name_with_casing
- Same as nt_to_unix_file_name, but additionally return unix file name
- without path, with the original casing from the NT file name.
- */
+static NTSTATUS nt_to_unix_file_name_with_casing( const OBJECT_ATTRIBUTES *attr, char **name_ret,
char **casing_ret, UINT disposition )
+{
- int len = attr->ObjectName->Length / sizeof(WCHAR);
- const WCHAR *nt_filename;
- NTSTATUS status;
- char *casing;
- /* NT name may not be NUL terminated; look for last \ character */
- for (nt_filename = attr->ObjectName->Buffer + len; nt_filename != attr->ObjectName->Buffer; nt_filename--)
if (nt_filename[-1] == '\\')
break;
- len = attr->ObjectName->Buffer + len - nt_filename;
I was about to sign it off as it looks good to me, but then I wondered what would happen if there's a trailing '\' in the name buffer. I don't think there's any guarantee the input is sane.
I quickly checked what the new tests do in such case, and on Windows the ntdll link tests seems to be happily doing something with such trailing backslash (haven't tried the other). I don't know if it treats the path as a folder or something else.
It also looks like nt_to_unix_file_name is already handling the case somehow, as it strips the trailing backslash from the unix_name (while keeping it in the nt_name buffer). I don't know if that's on purpose or not.
Maybe it could return the proper casing for the last path component too as it has more knowledge of what is been done?
On 4/26/21 4:03 PM, Rémi Bernon wrote:
Maybe it could return the proper casing for the last path component too as it has more knowledge of what is been done?
(Or the helper could simply first skip any trailing backslash if it's what we are supposed to do)
On Mon, Apr 26, 2021 at 04:03:50PM +0200, Rémi Bernon wrote:
On 4/23/21 5:52 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
nt_to_unix_file_name_with_casing
- Same as nt_to_unix_file_name, but additionally return unix file name
- without path, with the original casing from the NT file name.
Another nit-pick. "casing" is the process of changing a character's case, the correct term here is "original case". Llikewise the function name, param names, and variables should be changed to reflect that.
- */
+static NTSTATUS nt_to_unix_file_name_with_casing( const OBJECT_ATTRIBUTES *attr, char **name_ret,
char **casing_ret, UINT disposition )
+{
- int len = attr->ObjectName->Length / sizeof(WCHAR);
- const WCHAR *nt_filename;
- NTSTATUS status;
- char *casing;
Huw.
On 26/04/2021 17:03, Rémi Bernon wrote:
On 4/23/21 5:52 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- * nt_to_unix_file_name_with_casing
- Same as nt_to_unix_file_name, but additionally return unix file name
- without path, with the original casing from the NT file name.
- */
+static NTSTATUS nt_to_unix_file_name_with_casing( const OBJECT_ATTRIBUTES *attr, char **name_ret, + char **casing_ret, UINT disposition ) +{ + int len = attr->ObjectName->Length / sizeof(WCHAR); + const WCHAR *nt_filename; + NTSTATUS status; + char *casing;
+ /* NT name may not be NUL terminated; look for last \ character */ + for (nt_filename = attr->ObjectName->Buffer + len; nt_filename != attr->ObjectName->Buffer; nt_filename--) + if (nt_filename[-1] == '\') + break; + len = attr->ObjectName->Buffer + len - nt_filename;
I was about to sign it off as it looks good to me, but then I wondered what would happen if there's a trailing '\' in the name buffer. I don't think there's any guarantee the input is sane.
I quickly checked what the new tests do in such case, and on Windows the ntdll link tests seems to be happily doing something with such trailing backslash (haven't tried the other). I don't know if it treats the path as a folder or something else.
It also looks like nt_to_unix_file_name is already handling the case somehow, as it strips the trailing backslash from the unix_name (while keeping it in the nt_name buffer). I don't know if that's on purpose or not.
Maybe it could return the proper casing for the last path component too as it has more knowledge of what is been done?
I can just strip the trailing '\'. Even if it's a directory, it's still necessary, directories have case differences too. What we're interested in isn't whether it's a directory or a file, rather the last component.
Personally I'd rather not change nt_to_unix_file_name since it is used in many other places and files, it would complicate things in my opinion. This new helper is defined in the same file so it makes sense to also have knowledge of such things.