From 8ca5d4af90e3d8df0cbe188125f1758435f09a9a Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Tue, 10 Mar 2020 22:38:26 -0500 Subject: [PATCH] kernel32: Reimplement MoveFileWithProgress() on top of NtSetInformationFile(FileRenameInformation). Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- 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 ); -- 2.11.4.GIT