From: Duy Pham Duc phamducduy6122011@gmail.com
--- dlls/kernel32/tests/file.c | 44 ++++++- dlls/kernelbase/file.c | 197 +++++++++++++++++++++++++++++++- dlls/ntoskrnl.exe/tests/utils.h | 2 +- 3 files changed, 233 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 8e5a9511f76..b639ac2f710 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -396,7 +396,7 @@ static void test__lcreat( void ) ok( SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL ) != 0, "couldn't change attributes on file\n" ); todo_wine ok( DeleteFileA( filename ) != 0, "now it should be possible to delete the file\n" ); - + filehandle = _lcreat( filename, 2 ); ok( HFILE_ERROR != filehandle, "couldn't create file "%s" (err=%ld)\n", filename, GetLastError( ) );
@@ -952,6 +952,42 @@ static void test_CopyFileW(void) ok(ret, "DeleteFileW: error %ld\n", GetLastError()); }
+static COPYFILE2_MESSAGE_ACTION CopyFile2_Routine(const COPYFILE2_MESSAGE* pMessage, PVOID pvCallbackContext) +{ + switch(pMessage->Type) { + case COPYFILE2_CALLBACK_STREAM_STARTED: + trace("Stream %lu was started. Size: %llu bytes.\n", + pMessage->Info.StreamStarted.dwStreamNumber, + pMessage->Info.StreamStarted.uliStreamSize.QuadPart + ); + break; + + case COPYFILE2_CALLBACK_CHUNK_STARTED: + trace("Chunk %llu was started. Size: %llu bytes.\n", + pMessage->Info.ChunkStarted.uliChunkNumber.QuadPart, + pMessage->Info.ChunkStarted.uliChunkSize.QuadPart + ); + break; + + case COPYFILE2_CALLBACK_CHUNK_FINISHED: + trace("Chunk %llu was finished. Transfered Total: %llu bytes.\n", + pMessage->Info.ChunkFinished.uliChunkNumber.QuadPart, + pMessage->Info.ChunkFinished.uliStreamBytesTransferred.QuadPart + ); + break; + + case COPYFILE2_CALLBACK_STREAM_FINISHED: + trace("Stream %lu was finished. Copied: %llu bytes\n", + pMessage->Info.StreamFinished.dwStreamNumber, + pMessage->Info.StreamFinished.uliStreamBytesTransferred.QuadPart + ); + break; + default: + break; + } + return COPYFILE2_PROGRESS_CONTINUE; +} + static void test_CopyFile2(void) { static const WCHAR doesntexistW[] = {'d','o','e','s','n','t','e','x','i','s','t',0}; @@ -963,7 +999,7 @@ static void test_CopyFile2(void) DWORD ret, len; char buf[10]; HRESULT hr; - + if (!pCopyFile2) { todo_wine win_skip("CopyFile2 is not available\n"); @@ -984,12 +1020,13 @@ static void test_CopyFile2(void) memset(¶ms, 0, sizeof(params)); params.dwSize = sizeof(params); params.dwCopyFlags = COPY_FILE_FAIL_IF_EXISTS; + params.pProgressRoutine = CopyFile2_Routine;
SetLastError(0xdeadbeef); hr = pCopyFile2(source, dest, ¶ms); ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "CopyFile2: unexpected error 0x%08lx\n", hr); ok(GetLastError() == ERROR_FILE_EXISTS, "CopyFile2: last error %ld\n", GetLastError()); - + /* don't fail if exists */ params.dwSize = sizeof(params); params.dwCopyFlags = 0; @@ -1213,6 +1250,7 @@ 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); + trace("retok: 0x%08x\n", retok); todo_wine ok(!retok, "CopyFileExA unexpectedly succeeded\n"); todo_wine diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 442dc70b449..b69edcf8288 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -505,7 +505,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH AreFileApisANSI(void) */ static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDED_PARAMETERS *params ) { - DWORD flags = params ? params->dwCopyFlags : 0; + DWORD flags = params ? params->dwCopyFlags : 0; BOOL *cancel_ptr = params ? params->pfCancel : NULL; PCOPYFILE2_PROGRESS_ROUTINE progress = params ? params->pProgressRoutine : NULL;
@@ -516,12 +516,14 @@ static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDE DWORD count; BOOL ret = FALSE; char *buffer; + ULARGE_INTEGER file_size = {0}; + ULONGLONG bytes_transfered = 0; + ULONGLONG chunk_id = 0; + BOOL delete_dest = FALSE;
if (cancel_ptr) FIXME("pfCancel is not supported\n"); - if (progress) - FIXME("PCOPYFILE2_PROGRESS_ROUTINE is not supported\n"); - + if (!source || !dest) { SetLastError( ERROR_INVALID_PARAMETER ); @@ -547,6 +549,13 @@ static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDE { WARN("Unable to open source %s\n", debugstr_w(source)); HeapFree( GetProcessHeap(), 0, buffer ); + if(progress) { + COPYFILE2_MESSAGE msg = {0}; + msg.Type = COPYFILE2_CALLBACK_ERROR; + msg.Info.Error.CopyPhase = COPYFILE2_PHASE_PREPARE_SOURCE; + msg.Info.Error.hrFailure = HRESULT_FROM_WIN32(GetLastError()); + progress(&msg, params->pvCallbackContext); + } return FALSE; }
@@ -583,22 +592,138 @@ static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDE WARN("Unable to open dest %s\n", debugstr_w(dest)); HeapFree( GetProcessHeap(), 0, buffer ); CloseHandle( h1 ); + if(progress) + { + COPYFILE2_MESSAGE msg = {0}; + msg.Type = COPYFILE2_CALLBACK_ERROR; + msg.Info.Error.CopyPhase = COPYFILE2_PHASE_PREPARE_DEST; + msg.Info.Error.hrFailure = HRESULT_FROM_WIN32(GetLastError()); + progress(&msg, params->pvCallbackContext); + } return FALSE; }
+ if(progress) + { + COPYFILE2_MESSAGE msg = {0}; + + LARGE_INTEGER lFileSize = {0}; + if(GetFileSizeEx(h1, &lFileSize) != 0) + file_size.QuadPart = (ULONGLONG)lFileSize.QuadPart; + + msg.Type = COPYFILE2_CALLBACK_STREAM_STARTED; + /* One default stream unless add support for Alternate File Stream, default stream is "::$DATA" */ + msg.Info.StreamStarted.dwStreamNumber = 1; + msg.Info.StreamStarted.hSourceFile = h1; + msg.Info.StreamStarted.hDestinationFile = h2; + msg.Info.StreamStarted.uliStreamSize = + msg.Info.StreamStarted.uliTotalFileSize = file_size; + progress(&msg, params->pvCallbackContext); + } + while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count) { char *p = buffer; + DWORD chunk_size = count; + if(progress) + { + COPYFILE2_MESSAGE msg = {0}; + msg.Type = COPYFILE2_CALLBACK_CHUNK_STARTED; + msg.Info.ChunkStarted.dwStreamNumber = 1; + msg.Info.ChunkStarted.hSourceFile = h1; + msg.Info.ChunkStarted.hDestinationFile = h2; + msg.Info.ChunkStarted.uliChunkNumber.QuadPart = chunk_id; + msg.Info.ChunkStarted.uliChunkSize.QuadPart = (ULONGLONG)chunk_size; + msg.Info.ChunkStarted.uliStreamSize = + msg.Info.ChunkStarted.uliTotalFileSize = + file_size; + switch(progress(&msg, params->pvCallbackContext)) + { + case COPYFILE2_PROGRESS_STOP: + /* Fall-through */ + case COPYFILE2_PROGRESS_CANCEL: + ret = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED); + goto done; + case COPYFILE2_PROGRESS_PAUSE: + ret = HRESULT_FROM_WIN32(ERROR_REQUEST_PAUSED); + goto done; + case COPYFILE2_PROGRESS_QUIET: + progress = 0; + break; + default: + break; + } + } while (count != 0) { DWORD res; if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done; p += res; count -= res; + bytes_transfered += res; + } + if(progress) + { + COPYFILE2_MESSAGE msg = {0}; + msg.Type = COPYFILE2_CALLBACK_CHUNK_FINISHED; + msg.Info.ChunkFinished.dwStreamNumber = 1; + msg.Info.ChunkFinished.hSourceFile = h1; + msg.Info.ChunkFinished.hDestinationFile = h2; + msg.Info.ChunkFinished.uliChunkNumber.QuadPart = chunk_id; + msg.Info.ChunkFinished.uliChunkSize.QuadPart = (ULONGLONG)chunk_size; + msg.Info.ChunkFinished.uliStreamSize = + msg.Info.ChunkFinished.uliTotalFileSize = + file_size; + msg.Info.ChunkFinished.uliStreamBytesTransferred.QuadPart = + msg.Info.ChunkFinished.uliTotalBytesTransferred.QuadPart = + bytes_transfered; + chunk_id++; + switch(progress(&msg, params->pvCallbackContext)) + { + case COPYFILE2_PROGRESS_STOP: + if(params->dwCopyFlags & COPY_FILE_RESTARTABLE) + FIXME("COPY_FILE_RESTARTABLE flag is unsupported"); + /* Fall-through */ + case COPYFILE2_PROGRESS_CANCEL: + ret = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED); + goto done; + case COPYFILE2_PROGRESS_PAUSE: + ret = HRESULT_FROM_WIN32(ERROR_REQUEST_PAUSED); + goto done; + case COPYFILE2_PROGRESS_QUIET: + progress = 0; + goto done; + default: + break; + } } } ret = TRUE; done: + if(progress) + { + COPYFILE2_MESSAGE msg = {0}; + LARGE_INTEGER liZero = {0}; + LARGE_INTEGER liPos = {0}; + + SetFilePointerEx(h1, liZero, &liPos, FILE_CURRENT); + + msg.Type = COPYFILE2_CALLBACK_STREAM_FINISHED; + msg.Info.StreamFinished.dwStreamNumber = 1; + msg.Info.StreamFinished.hSourceFile = h1; + msg.Info.StreamFinished.hDestinationFile = h2; + msg.Info.StreamFinished.uliStreamSize = + msg.Info.StreamFinished.uliTotalFileSize = file_size; + msg.Info.StreamFinished.uliStreamBytesTransferred.QuadPart = + msg.Info.StreamFinished.uliTotalBytesTransferred.QuadPart = + (ULONGLONG)liPos.QuadPart; + if(progress(&msg, params->pvCallbackContext) == COPYFILE2_PROGRESS_CANCEL) + { + delete_dest = TRUE; + ret = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED); + } + } + /* Maintain the timestamp of source file to destination file and read-only attribute */ info.FileAttributes &= FILE_ATTRIBUTE_READONLY; NtSetInformationFile( h2, &io, &info, sizeof(info), FileBasicInformation ); @@ -606,6 +731,8 @@ done: CloseHandle( h1 ); CloseHandle( h2 ); if (ret) SetLastError( 0 ); + if(delete_dest) + DeleteFileW(dest); return ret; }
@@ -617,7 +744,64 @@ HRESULT WINAPI CopyFile2( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTE return copy_file(source, dest, params) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); }
+struct copyfile_compat_data { + LPPROGRESS_ROUTINE progress; + void *progress_param; + LARGE_INTEGER file_size; + LARGE_INTEGER total_transfered; +}; +/****************************************************************************** + * copyfile_filter_retval + * + * Filter return value of LPPROGRESS_ROUTINE to match how copy_file expected + */ +static inline COPYFILE2_MESSAGE_ACTION copyfile_filter_retval(const DWORD value) +{ + if(value > PROGRESS_QUIET) + return COPYFILE2_PROGRESS_CONTINUE; + return (COPYFILE2_MESSAGE_ACTION)value; +} +/****************************************************************************** + * copy_file_compat_func + * + * A function to use LPPROGRESS_ROUTINE of CopyFileEx in copy_file, which expected PCOPYFILE2_PROGRESS_ROUTINE + */ +static COPYFILE2_MESSAGE_ACTION copyfile_compat_func(const COPYFILE2_MESSAGE *msg, void *ctx) +{ + struct copyfile_compat_data *data = ctx; + switch(msg->Type) + { + case COPYFILE2_CALLBACK_CHUNK_FINISHED: { + LARGE_INTEGER file_bytes_transfered; + LARGE_INTEGER stream_size; + LARGE_INTEGER stream_bytes_transfered; + file_bytes_transfered.QuadPart = (LONGLONG)msg->Info.ChunkFinished.uliTotalBytesTransferred.QuadPart; + stream_size.QuadPart = (LONGLONG)msg->Info.ChunkFinished.uliStreamSize.QuadPart; + stream_bytes_transfered.QuadPart = (LONGLONG)msg->Info.ChunkFinished.uliStreamBytesTransferred.QuadPart; + return copyfile_filter_retval(data->progress(data->file_size, file_bytes_transfered, stream_size, stream_bytes_transfered, + msg->Info.ChunkFinished.dwStreamNumber, CALLBACK_CHUNK_FINISHED, + msg->Info.ChunkFinished.hSourceFile, msg->Info.ChunkFinished.hDestinationFile, data->progress_param)); + } + + case COPYFILE2_CALLBACK_STREAM_STARTED: { + LARGE_INTEGER stream_size; + LARGE_INTEGER zero = {0}; + stream_size.QuadPart = (LONGLONG)msg->Info.StreamStarted.uliStreamSize.QuadPart;
+ data->file_size.QuadPart = (LONGLONG)msg->Info.StreamStarted.uliTotalFileSize.QuadPart; + return data->progress(data->file_size, data->total_transfered, stream_size, zero, msg->Info.StreamStarted.dwStreamNumber, + CALLBACK_STREAM_SWITCH, msg->Info.StreamStarted.hSourceFile, msg->Info.StreamStarted.hDestinationFile, data->progress_param); + } + + case COPYFILE2_CALLBACK_STREAM_FINISHED: { + data->total_transfered.QuadPart = (LONGLONG)msg->Info.StreamFinished.uliTotalBytesTransferred.QuadPart; + /* Fall-through */ + } + + default: + return COPYFILE2_PROGRESS_CONTINUE; + } +} /*********************************************************************** * CopyFileExW (kernelbase.@) */ @@ -625,6 +809,7 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT void *param, BOOL *cancel_ptr, DWORD flags ) { COPYFILE2_EXTENDED_PARAMETERS params; + struct copyfile_compat_data data = { progress, param };
if (progress) FIXME("LPPROGRESS_ROUTINE is not supported\n"); @@ -633,8 +818,8 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
params.dwSize = sizeof(params); params.dwCopyFlags = flags; - params.pProgressRoutine = NULL; - params.pvCallbackContext = NULL; + params.pProgressRoutine = progress ? copyfile_compat_func : NULL; + params.pvCallbackContext = &data; params.pfCancel = NULL;
return copy_file( source, dest, ¶ms ); diff --git a/dlls/ntoskrnl.exe/tests/utils.h b/dlls/ntoskrnl.exe/tests/utils.h index 2d904f549ce..cca4df3de07 100644 --- a/dlls/ntoskrnl.exe/tests/utils.h +++ b/dlls/ntoskrnl.exe/tests/utils.h @@ -48,7 +48,7 @@ const char *winetest_platform; int winetest_platform_is_wine; int winetest_debug; int winetest_report_success; -int winetest_color = 0; +int winetest_color = 1; int winetest_time = 0; int winetest_start_time, winetest_last_time;