From: Michael Müller michael@fds-team.de
v2: Rebase to git after long type changes.
Signed-off-by: Alistair Leslie-Hughes leslie_alistair@hotmail.com --- dlls/kernel32/tests/file.c | 6 --- dlls/kernelbase/file.c | 77 +++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 77174d43d5b..4bd9f790cc0 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1170,23 +1170,17 @@ static void test_CopyFileEx(void) ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %ld\n", GetLastError()); SetLastError(0xdeadbeef); retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0); - todo_wine ok(!retok, "CopyFileExA unexpectedly succeeded\n"); - todo_wine ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %ld\n", GetLastError()); ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file was deleted\n");
hfile = CreateFileA(dest, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, 0); - todo_wine ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %ld\n", GetLastError()); SetLastError(0xdeadbeef); retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0); - todo_wine ok(!retok, "CopyFileExA unexpectedly succeeded\n"); - todo_wine ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %ld\n", GetLastError()); - todo_wine ok(GetFileAttributesA(dest) == INVALID_FILE_ATTRIBUTES, "file was not deleted\n");
retok = CopyFileExA(source, NULL, copy_progress_cb, hfile, NULL, 0); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index c6dc904044a..b9ba1bd7c26 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -499,11 +499,16 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT { static const int buffer_size = 65536; HANDLE h1, h2; - FILE_BASIC_INFORMATION info; + FILE_NETWORK_OPEN_INFORMATION info; + FILE_BASIC_INFORMATION basic_info; IO_STATUS_BLOCK io; DWORD count; BOOL ret = FALSE; char *buffer; + LARGE_INTEGER size; + LARGE_INTEGER transferred; + DWORD cbret; + DWORD source_access = GENERIC_READ;
if (!source || !dest) { @@ -518,7 +523,15 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
TRACE("%s -> %s, %lx\n", debugstr_w(source), debugstr_w(dest), flags);
- if ((h1 = CreateFileW( source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + if (flags & COPY_FILE_RESTARTABLE) + FIXME("COPY_FILE_RESTARTABLE is not supported\n"); + if (flags & COPY_FILE_COPY_SYMLINK) + FIXME("COPY_FILE_COPY_SYMLINK is not supported\n"); + + if (flags & COPY_FILE_OPEN_SOURCE_FOR_WRITE) + source_access |= GENERIC_WRITE; + + if ((h1 = CreateFileW( source, source_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) { WARN("Unable to open source %s\n", debugstr_w(source)); @@ -526,7 +539,7 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT return FALSE; }
- if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileBasicInformation ))) + if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileNetworkOpenInformation ))) { WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source)); HeapFree( GetProcessHeap(), 0, buffer ); @@ -552,7 +565,11 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT } }
- if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + if ((h2 = CreateFileW( dest, GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS, + info.FileAttributes, h1 )) == INVALID_HANDLE_VALUE && + /* retry without DELETE if we got a sharing violation */ + (h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS, info.FileAttributes, h1 )) == INVALID_HANDLE_VALUE) { @@ -562,6 +579,29 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT return FALSE; }
+ size = info.EndOfFile; + transferred.QuadPart = 0; + + if (progress) + { + cbret = progress( size, transferred, size, transferred, 1, + CALLBACK_STREAM_SWITCH, h1, h2, param ); + if (cbret == PROGRESS_QUIET) + progress = NULL; + else if (cbret == PROGRESS_STOP) + { + SetLastError( ERROR_REQUEST_ABORTED ); + goto done; + } + else if (cbret == PROGRESS_CANCEL) + { + BOOLEAN disp = TRUE; + SetFileInformationByHandle( h2, FileDispositionInfo, &disp, sizeof(disp) ); + SetLastError( ERROR_REQUEST_ABORTED ); + goto done; + } + } + while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count) { char *p = buffer; @@ -571,13 +611,38 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done; p += res; count -= res; + + if (progress) + { + transferred.QuadPart += res; + cbret = progress( size, transferred, size, transferred, 1, + CALLBACK_CHUNK_FINISHED, h1, h2, param ); + if (cbret == PROGRESS_QUIET) + progress = NULL; + else if (cbret == PROGRESS_STOP) + { + SetLastError( ERROR_REQUEST_ABORTED ); + goto done; + } + else if (cbret == PROGRESS_CANCEL) + { + BOOLEAN disp = TRUE; + SetFileInformationByHandle( h2, FileDispositionInfo, &disp, sizeof(disp) ); + SetLastError( ERROR_REQUEST_ABORTED ); + goto done; + } + } } } ret = TRUE; done: /* Maintain the timestamp of source file to destination file */ - info.FileAttributes = 0; - NtSetInformationFile( h2, &io, &info, sizeof(info), FileBasicInformation ); + basic_info.CreationTime = info.CreationTime; + basic_info.LastAccessTime = info.LastAccessTime; + basic_info.LastWriteTime = info.LastWriteTime; + basic_info.ChangeTime = info.ChangeTime; + basic_info.FileAttributes = 0; + NtSetInformationFile( h2, &io, &basic_info, sizeof(basic_info), FileBasicInformation ); HeapFree( GetProcessHeap(), 0, buffer ); CloseHandle( h1 ); CloseHandle( h2 );