351 lines
10 KiB
C++
351 lines
10 KiB
C++
|
|
//+============================================================================
|
|
//
|
|
// 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 <windows.h>
|
|
#include <stdio.h>
|
|
#include <process.h>
|
|
#include <winbasep.h>
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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"<Unknown>" ));
|
|
}
|
|
|
|
return( PRIVPROGRESS_REASON_NOT_HANDLED );
|
|
|
|
}
|
|
|
|
void
|
|
Usage()
|
|
{
|
|
printf( "\n Purpose: Call the CopyFile API\n"
|
|
" Usage: CopyFAPI [options] <source> <dest>\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() );
|
|
}
|
|
|
|
|
|
}
|