//+============================================================================ // // CopyFAPI.cxx // // This program provides a very simple wrapper of the CopyFileEx API // (and the PrivCopyFileEx in NT5) with no extra functionality. // //+============================================================================ #define UNICODE #define _UNICODE #include #include #include #include //+---------------------------------------------------------------------------- // // PromptForNotSupported // // When the PrivCopyFileEx callback function is called to inform the caller // that something couldn't be copied, this routine is used to prompt the // user of this utility to see if we should continue. // //+---------------------------------------------------------------------------- struct { WCHAR wc; DWORD dwProgress; } rgPromptResponse[] = { {L'O', PROGRESS_CONTINUE}, {L'A', PROGRESS_CANCEL}, {L'S', PROGRESS_STOP}, {L'Q', PROGRESS_QUIET}, {L'N', PRIVPROGRESS_REASON_NOT_HANDLED} }; DWORD PromptForNotSupported( LPWSTR pwszPrompt ) { WCHAR wc = 'z'; HANDLE hKeyboard = INVALID_HANDLE_VALUE; ULONG KeyboardModeNew, KeyboardModeOld; hKeyboard = CreateFile( (LPWSTR)L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if( INVALID_HANDLE_VALUE != hKeyboard && !IsDebuggerPresent() && GetConsoleMode( hKeyboard, &KeyboardModeOld ) ) { KeyboardModeNew = KeyboardModeOld & ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT); SetConsoleMode( hKeyboard, KeyboardModeNew ); } while( TRUE ) { for( int i = 0; i < sizeof(rgPromptResponse)/sizeof(rgPromptResponse[0]); i++ ) { if( wc == rgPromptResponse[i].wc ) return( rgPromptResponse[i].dwProgress ); } wprintf( L"%s (cOntinue, cAncel, Stop, Quiet, Not handled) ", pwszPrompt ); wc = getwchar(); wprintf( L"\r" ); wprintf( L" \r" ); } SetConsoleMode( hKeyboard, KeyboardModeOld ); CloseHandle( hKeyboard ); return( PROGRESS_CONTINUE ); // Should never execute } //+---------------------------------------------------------------------------- // // CopyFileProgressRoutine // // This is the callback function given to CopyFileEx (if so desired by // the user). It displays the progress information, and prompts the // user for permission to continue if something (e.g. ACLs) can't be // copied. // //+---------------------------------------------------------------------------- DWORD WINAPI CopyFileProgressRoutine( LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData OPTIONAL ) { wprintf( L"Progress: %7I64i, %7I64i, %7I64i, %7I64i, %7d\n", TotalFileSize.QuadPart, TotalBytesTransferred.QuadPart, StreamSize, StreamBytesTransferred, dwStreamNumber ); switch( dwCallbackReason ) { case PRIVCALLBACK_STREAMS_NOT_SUPPORTED: return( PromptForNotSupported( L"Streams not supported" )); case PRIVCALLBACK_SECURITY_INFORMATION_NOT_SUPPORTED: return( PromptForNotSupported( L"Security info not supported" )); case PRIVCALLBACK_COMPRESSION_NOT_SUPPORTED: return( PromptForNotSupported( L"Compression not supported" )); case PRIVCALLBACK_COMPRESSION_FAILED: return( PromptForNotSupported( L"Compression failed" )); case PRIVCALLBACK_ENCRYPTION_NOT_SUPPORTED: return( PromptForNotSupported( L"Encryption not supported" )); case PRIVCALLBACK_CANT_ENCRYPT_SYSTEM_FILE: return( PromptForNotSupported( L"Can't encrypt a system file" )); case PRIVCALLBACK_ENCRYPTION_FAILED: return( PromptForNotSupported( L"Encryption failed" )); case PRIVCALLBACK_EAS_NOT_SUPPORTED: return( PromptForNotSupported( L"EAs not supported" )); case PRIVCALLBACK_SPARSE_NOT_SUPPORTED: return( PromptForNotSupported( L"Sparse not supported" )); case PRIVCALLBACK_SPARSE_FAILED: return( PromptForNotSupported( L"Sparse failed" )); case PRIVCALLBACK_DACL_ACCESS_DENIED: return( PromptForNotSupported( L"DACL access denied" )); case PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED: return( PromptForNotSupported( L"Owner/group access denied" )); case PRIVCALLBACK_OWNER_GROUP_FAILED: return( PromptForNotSupported( L"Owner/group failed" )); case PRIVCALLBACK_SACL_ACCESS_DENIED: return( PromptForNotSupported( L"SACL access denied" )); case CALLBACK_CHUNK_FINISHED: case CALLBACK_STREAM_SWITCH: return( PROGRESS_CONTINUE ); default: return( PromptForNotSupported( L"" )); } return( PRIVPROGRESS_REASON_NOT_HANDLED ); } void Usage() { printf( "\n Purpose: Call the CopyFile API\n" " Usage: CopyFAPI [options] \n" " Options: -f COPY_FILE_FAIL_IF_EXISTS\n" " -r COPY_FILE_RESTARTABLE\n" " -e COPY_FILE_ALLOW_DECRYPTED_DESTINATION\n" " -m PRIVCOPY_FILE_METADATA\n" " -s PRIVCOPY_FILE_SACL\n" " -u PRIVCOPY_FILE_SUPERSEDE\n" " -o PRIVCOPY_FILE_OWNER_GROUP\n" " -d PRIVCOPY_FILE_DIRECTORY\n" " -b PRIVCOPY_FILE_BACKUP_SEMANTICS\n" " -c Use the callback function\n" " Note: Since this simply calls the CopyFile API,\n" " you must specify the file path (not just\n" " the parent directory), and wildcards\n" " are not allowed\n" ); } typedef BOOL (__stdcall *PFNMoveFileIdentityW)( LPCWSTR lpOldFileName, LPCWSTR lpNewFileName, DWORD dwFlags ); typedef BOOL (__stdcall *PFNPrivCopyFileExW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL, LPVOID lpData OPTIONAL, LPBOOL pbCancel OPTIONAL, DWORD dwCopyFlags ); //+---------------------------------------------------------------------------- // // wmain // // User parameters are mapped to CopyFileEx parameters, then the API // is called. // //+---------------------------------------------------------------------------- extern "C" void __cdecl wmain( int cArgs, WCHAR *rgpwszArgs[] ) { LONG iArgs; DWORD dwCopyFileFlags = 0; BOOL fUseCallback = FALSE; cArgs--; iArgs = 1; while( cArgs > 0 ) { if( L'-' != rgpwszArgs[iArgs][0] && L'/' != rgpwszArgs[iArgs][0] ) { break; } WCHAR wcUpper = towupper( rgpwszArgs[iArgs][1] ); switch( wcUpper ) { case 'F': dwCopyFileFlags |= COPY_FILE_FAIL_IF_EXISTS; break; case 'R': dwCopyFileFlags |= COPY_FILE_RESTARTABLE; break; case 'E': dwCopyFileFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; break; case 'M': dwCopyFileFlags |= PRIVCOPY_FILE_METADATA; break; case 'S': dwCopyFileFlags |= PRIVCOPY_FILE_SACL; break; case 'U': dwCopyFileFlags |= PRIVCOPY_FILE_SUPERSEDE; break; case 'O': dwCopyFileFlags |= PRIVCOPY_FILE_OWNER_GROUP; break; case 'D': dwCopyFileFlags |= PRIVCOPY_FILE_DIRECTORY; break; case 'B': dwCopyFileFlags |= PRIVCOPY_FILE_BACKUP_SEMANTICS; break; case 'C': fUseCallback = TRUE; break; case 'X': dwCopyFileFlags |= 0x80; break; case 'P': dwCopyFileFlags |= 0x100; break; default: wprintf( L"Invalid option: %c\n", wcUpper ); Usage(); exit(0); } iArgs++; cArgs--; } if( cArgs != 2 ) { Usage(); exit(0); } if( fUseCallback ) wprintf( L" cbTotal cbCur cbStm cbStmCur StmNum\n" ); try { if( PRIVCOPY_FILE_VALID_FLAGS & dwCopyFileFlags ) { // We need to call the private API PFNPrivCopyFileExW pfnPrivCopyFileExW; pfnPrivCopyFileExW = (PFNPrivCopyFileExW) GetProcAddress( GetModuleHandle(L"kernel32.dll"), "PrivCopyFileExW" ); if( NULL == pfnPrivCopyFileExW ) throw L"Couldn't get PrivCopyFileExW export"; if( !pfnPrivCopyFileExW( rgpwszArgs[iArgs], rgpwszArgs[iArgs+1], fUseCallback ? CopyFileProgressRoutine : NULL, NULL, NULL, dwCopyFileFlags )) throw L"PrivCopyFileEx failed"; else wprintf( L"Succeeded\n" ); } else { // We can use the public API if( !CopyFileExW( rgpwszArgs[iArgs], rgpwszArgs[iArgs+1], fUseCallback ? CopyFileProgressRoutine : NULL, NULL, NULL, dwCopyFileFlags )) throw L"CopyFileEx failed"; else wprintf( L"Succeeded\n" ); } } catch( const WCHAR *pwszError ) { wprintf( L"Error: %s (%lu)\n", pwszError, GetLastError() ); } }