Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/tests/file.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index e0d4580ae04..9e54cf044d7 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2759,6 +2759,36 @@ static void test_file_link_information(void) HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); delete_object( newpath ); + + /* oldpath == newpath */ + 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" ); + + 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 ); + todo_wine ok( U(io).Status == STATUS_SUCCESS, "got io status %#x\n", U(io).Status ); + todo_wine ok( res == STATUS_SUCCESS, "got status %x\n", res ); + ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); }
static void test_file_both_information(void)
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/file.c | 16 ++-------------- server/fd.c | 10 ++++++++-- server/protocol.def | 1 + 3 files changed, 11 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 5ecf293820e..f59e737baa5 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2778,18 +2778,12 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE) break;
- if (!info->ReplaceIfExists && io->u.Status == STATUS_SUCCESS) - { - RtlFreeAnsiString( &unix_name ); - io->u.Status = STATUS_OBJECT_NAME_COLLISION; - break; - } - SERVER_START_REQ( set_fd_name_info ) { req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->link = FALSE; + req->replace = info->ReplaceIfExists; wine_server_add_data( req, unix_name.Buffer, unix_name.Length ); io->u.Status = wine_server_call( req ); } @@ -2821,18 +2815,12 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, if (io->u.Status != STATUS_SUCCESS && io->u.Status != STATUS_NO_SUCH_FILE) break;
- if (!info->ReplaceIfExists && io->u.Status == STATUS_SUCCESS) - { - RtlFreeAnsiString( &unix_name ); - io->u.Status = STATUS_OBJECT_NAME_COLLISION; - break; - } - SERVER_START_REQ( set_fd_name_info ) { req->handle = wine_server_obj_handle( handle ); req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->link = TRUE; + req->replace = info->ReplaceIfExists; wine_server_add_data( req, unix_name.Buffer, unix_name.Length ); io->u.Status = wine_server_call( req ); } diff --git a/server/fd.c b/server/fd.c index 5d80e218b97..c2cb3c96ac6 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2325,7 +2325,7 @@ 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, int create_link ) + data_size_t len, int create_link, int replace ) { struct inode *inode; struct stat st; @@ -2367,6 +2367,12 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr,
if (!stat( name, &st )) { + if (!replace) + { + set_error( STATUS_OBJECT_NAME_COLLISION ); + goto failed; + } + /* can't replace directories or special files */ if (!S_ISREG( st.st_mode )) { @@ -2695,7 +2701,7 @@ DECL_HANDLER(set_fd_name_info)
if ((fd = get_handle_fd_obj( current->process, req->handle, 0 ))) { - set_fd_name( fd, root_fd, get_req_data(), get_req_data_size(), req->link ); + set_fd_name( fd, root_fd, get_req_data(), get_req_data_size(), 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 6c44b2b43f5..722993e59c7 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3809,6 +3809,7 @@ struct handle_info obj_handle_t handle; /* handle to a file or directory */ obj_handle_t rootdir; /* root directory */ int link; /* link instead of renaming */ + int replace; /* replace an existing file? */ VARARG(filename,string); /* new file name */ @END
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/tests/file.c | 8 ++++---- server/fd.c | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 9e54cf044d7..c05dba574f7 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2123,8 +2123,8 @@ static void test_file_rename_information(void)
U(io).Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == STATUS_SUCCESS, "got io status %#x\n", U(io).Status ); - todo_wine ok( res == STATUS_SUCCESS, "got status %x\n", res ); + 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 ); @@ -2782,8 +2782,8 @@ static void test_file_link_information(void) fli->ReplaceIfExists = TRUE; U(io).Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == STATUS_SUCCESS, "got io status %#x\n", U(io).Status ); - todo_wine ok( res == STATUS_SUCCESS, "got status %x\n", res ); + 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 ); diff --git a/server/fd.c b/server/fd.c index c2cb3c96ac6..1e2c69e50c9 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2328,7 +2328,7 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, data_size_t len, int create_link, int replace ) { struct inode *inode; - struct stat st; + struct stat st, st2; char *name;
if (!fd->inode || !fd->unix_name) @@ -2367,6 +2367,15 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr,
if (!stat( name, &st )) { + if (fd->unix_fd != -1 && !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 (!replace) { set_error( STATUS_OBJECT_NAME_COLLISION );
Same as MoveFileWithProgressW().
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- server/fd.c | 10 ++++++++++ server/file.c | 9 +++++++-- server/file.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/server/fd.c b/server/fd.c index 1e2c69e50c9..631cd180c29 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2428,6 +2428,16 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, goto failed; }
+ if (is_file_executable( fd->unix_name ) != is_file_executable( name ) && !fstat( fd->unix_fd, &st )) + { + if (is_file_executable( fd->unix_name )) + /* set executable bit where read bit is set */ + st.st_mode |= (st.st_mode & 0444) >> 2; + else + st.st_mode &= ~0111; + fchmod( fd->unix_fd, st.st_mode ); + } + free( fd->unix_name ); fd->unix_name = name; fd->closed->unix_name = name; diff --git a/server/file.c b/server/file.c index 71b84486b0f..bce202138e0 100644 --- a/server/file.c +++ b/server/file.c @@ -191,6 +191,12 @@ static struct object *create_file_obj( struct fd *fd, unsigned int access, mode_ return &file->obj; }
+int is_file_executable( const char *name ) +{ + int len = strlen( name ); + return len >= 4 && (!strcasecmp( name + len - 4, ".exe") || !strcasecmp( name + len - 4, ".com" )); +} + static struct object *create_file( struct fd *root, const char *nameptr, data_size_t len, unsigned int access, unsigned int sharing, int create, unsigned int options, unsigned int attrs, @@ -236,8 +242,7 @@ static struct object *create_file( struct fd *root, const char *nameptr, data_si else mode = (attrs & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666;
- if (len >= 4 && - (!strcasecmp( name + len - 4, ".exe" ) || !strcasecmp( name + len - 4, ".com" ))) + if (is_file_executable( name )) { if (mode & S_IRUSR) mode |= S_IXUSR; diff --git a/server/file.h b/server/file.h index 4341ad3b040..0df4c177162 100644 --- a/server/file.h +++ b/server/file.h @@ -149,6 +149,7 @@ extern void file_set_error(void); extern struct object_type *file_get_type( struct object *obj ); extern struct security_descriptor *mode_to_sd( mode_t mode, const SID *user, const SID *group ); extern mode_t sd_to_mode( const struct security_descriptor *sd, const SID *owner ); +extern int is_file_executable( const char *name );
/* file mapping functions */
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/kernel32/path.c | 104 +++++++------------------------------ dlls/kernel32/tests/file.c | 8 +-- 2 files changed, 22 insertions(+), 90 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 617b14a7178..31652d3164b 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -448,14 +448,14 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, LPPROGRESS_ROUTINE fnProgress, LPVOID param, DWORD flag ) { + FILE_RENAME_INFORMATION *rename_info; FILE_BASIC_INFORMATION info; UNICODE_STRING nt_name; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; NTSTATUS status; - HANDLE source_handle = 0, dest_handle = 0; - ANSI_STRING source_unix, dest_unix; - DWORD options; + HANDLE source_handle = 0; + ULONG size;
TRACE("(%s,%s,%p,%p,%04x)\n", debugstr_w(source), debugstr_w(dest), fnProgress, param, flag ); @@ -473,8 +473,6 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, SetLastError( ERROR_PATH_NOT_FOUND ); return FALSE; } - source_unix.Buffer = NULL; - dest_unix.Buffer = NULL; attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.Attributes = OBJ_CASE_INSENSITIVE; @@ -484,8 +482,6 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest,
status = NtOpenFile( &source_handle, DELETE | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT ); - if (status == STATUS_SUCCESS) - status = wine_nt_to_unix_file_name( &nt_name, &source_unix, FILE_OPEN, FALSE ); RtlFreeUnicodeString( &nt_name ); if (status != STATUS_SUCCESS) { @@ -499,101 +495,37 @@ BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, goto error; }
- /* we must have write access to the destination, and it must */ - /* not exist except if MOVEFILE_REPLACE_EXISTING is set */ - if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL )) { SetLastError( ERROR_PATH_NOT_FOUND ); goto error; } - options = FILE_NON_DIRECTORY_FILE | 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)) - { - if (!is_same_file( source_handle, dest_handle )) - { - SetLastError( ERROR_ALREADY_EXISTS ); - RtlFreeUnicodeString( &nt_name ); - goto error; - } - } - else if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* cannot replace directory */ - { - SetLastError( ERROR_ACCESS_DENIED ); - goto error; - }
- NtClose( dest_handle ); - dest_handle = NULL; - } - else if (status != STATUS_OBJECT_NAME_NOT_FOUND) - { - SetLastError( RtlNtStatusToDosError(status) ); - RtlFreeUnicodeString( &nt_name ); + size = offsetof( FILE_RENAME_INFORMATION, FileName ) + nt_name.Length; + if (!(rename_info = HeapAlloc( GetProcessHeap(), 0, size ))) goto error; - }
- status = wine_nt_to_unix_file_name( &nt_name, &dest_unix, FILE_OPEN_IF, FALSE ); + rename_info->ReplaceIfExists = !!(flag & MOVEFILE_REPLACE_EXISTING); + rename_info->RootDirectory = NULL; + rename_info->FileNameLength = nt_name.Length; + memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length ); RtlFreeUnicodeString( &nt_name ); - if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE) - { - SetLastError( RtlNtStatusToDosError(status) ); - goto error; - } - - /* now perform the rename */ - - if (rename( source_unix.Buffer, dest_unix.Buffer ) == -1) + status = NtSetInformationFile( source_handle, &io, rename_info, size, FileRenameInformation ); + if (status == STATUS_NOT_SAME_DEVICE && (flag & MOVEFILE_COPY_ALLOWED)) { - if (errno == EXDEV && (flag & MOVEFILE_COPY_ALLOWED)) - { - NtClose( source_handle ); - RtlFreeAnsiString( &source_unix ); - RtlFreeAnsiString( &dest_unix ); - if (!CopyFileExW( source, dest, fnProgress, param, NULL, - flag & MOVEFILE_REPLACE_EXISTING ? - 0 : COPY_FILE_FAIL_IF_EXISTS )) - return FALSE; - return DeleteFileW( source ); - } - FILE_SetDosError(); - /* if we created the destination, remove it */ - if (io.Information == FILE_CREATED) unlink( dest_unix.Buffer ); - goto error; - } - - /* fixup executable permissions */ - - if (is_executable( source ) != is_executable( dest )) - { - struct stat fstat; - if (stat( dest_unix.Buffer, &fstat ) != -1) - { - if (is_executable( dest )) - /* set executable bit where read bit is set */ - fstat.st_mode |= (fstat.st_mode & 0444) >> 2; - else - fstat.st_mode &= ~0111; - chmod( dest_unix.Buffer, fstat.st_mode ); - } + NtClose( source_handle ); + if (!CopyFileExW( source, dest, fnProgress, param, NULL, + flag & MOVEFILE_REPLACE_EXISTING ? + 0 : COPY_FILE_FAIL_IF_EXISTS )) + return FALSE; + return DeleteFileW( source ); }
NtClose( source_handle ); - RtlFreeAnsiString( &source_unix ); - RtlFreeAnsiString( &dest_unix ); - return TRUE; + return set_ntstatus( status );
error: if (source_handle) NtClose( source_handle ); - if (dest_handle) NtClose( dest_handle ); - RtlFreeAnsiString( &source_unix ); - RtlFreeAnsiString( &dest_unix ); return FALSE; }
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index ad0c694f410..785bd60c237 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -5631,7 +5631,7 @@ static void test_move_file(void) SetLastError(0xdeadbeef); ret = MoveFileA( "winetest_dir3", "winetest_dir2" ); ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError()); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError());
file = CreateFileA( "winetest_file3", DELETE, 0, NULL, OPEN_EXISTING, 0, 0 ); ok(file != INVALID_HANDLE_VALUE, "failed to open file, error %u\n", GetLastError()); @@ -5658,14 +5658,14 @@ static void test_move_file(void) ok(file != INVALID_HANDLE_VALUE, "failed to open file, error %u\n", GetLastError()); SetLastError(0xdeadbeef); ret = MoveFileExA( "winetest_file2", "winetest_file1", MOVEFILE_REPLACE_EXISTING ); - todo_wine ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); CloseHandle( file );
SetLastError(0xdeadbeef); ret = MoveFileExA( "winetest_file2", "winetest_dir2", MOVEFILE_REPLACE_EXISTING ); ok(!ret, "expected failure\n"); - todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError());
SetLastError(0xdeadbeef); ret = MoveFileExA( "winetest_dir3", "winetest_dir2", MOVEFILE_REPLACE_EXISTING );
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=66906
Your paranoid android.
=== debiant (32 bit Chinese:China report) ===
kernel32: debugger.c:305: Test failed: GetThreadContext failed: 5