// fdump.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include "fs_io.h"
////////////////////////////////////////////////////////////////////////////
typedef BOOL ( CALLBACK* LPFNPARSEPARAM_CALLBACK )(
IN INT iArgCount,
IN INT iArgIndex,
IN INT iArgSubIndex,
IN LPCTSTR pszArgument,
IN BOOL bArgIsFlag,
IN LPVOID lpUserParam
);
typedef struct _USER_PARAMS
{
TCHAR szSrcFile[ _MAX_PATH + 1 ];
TCHAR szDstFile[ _MAX_PATH + 1 ];
BOOL bVerbose;
BOOL bHelp;
} USER_PARAMS, *PUSER_PARAMS, *LPUSER_PARAMS;
////////////////////////////////////////////////////////////////////////////
// Globals:
COORD g_curPos = { 0 };
COORD g_scrRect = { 0 };
HANDLE g_hEventBreak = NULL;
////////////////////////////////////////////////////////////////////////////
STDAPI_( BOOL ) ConsoleCtrlHandler( IN DWORD dwCtrlType );
VOID ConsoleGetCursorPos ( VOID );
VOID ConsoleRewindCursorPos ( VOID );
VOID PrintUsage ( VOID );
VOID PrintLine ( IN LPCTSTR pszName, IN LPCTSTR pszValFmt, IN INT nLineMax, IN ... );
VOID PrintVolInfo ( IN PFS_VOLUME_INFO pFsVolInfo );
VOID PrintFileInfo ( IN PFS_FILE_INFO pFsFileInfo );
VOID PrintPercentComplete( IN DWORD dwLcn, IN DWORD dwPercentComplete, IN BOOL bVerbose );
VOID PrintLastError ( IN DWORD dwJobType );
////////////////////////////////////////////////////////////////////////////
VOID ConsoleGetCursorPos( VOID )
{
CONSOLE_SCREEN_BUFFER_INFO csbi = { 0 };
GetConsoleScreenBufferInfo(
GetStdHandle( STD_OUTPUT_HANDLE ), &csbi
);
CopyMemory( &g_scrRect, &csbi.dwSize, sizeof( COORD ) );
CopyMemory( &g_curPos, &csbi.dwCursorPosition, sizeof( COORD ) );
}
VOID ConsoleRewindCursorPos( VOID )
{
// Clear the line:
FillConsoleOutputCharacter(
GetStdHandle( STD_OUTPUT_HANDLE ),
_T( ' ' ), g_scrRect.X, g_curPos, NULL
);
// Bring cursor back into initial position:
SetConsoleCursorPosition(
GetStdHandle( STD_OUTPUT_HANDLE ), g_curPos
);
}
// ConsoleCtrlHandler runs in different from main's thread, that is
// why we do not start new thread for FsSectorCopy - we already have
// different ones, one - main and one - ConsoleCtrlHandler:
STDMETHODIMP_( BOOL ) ConsoleCtrlHandler( IN DWORD dwCtrlType )
{
BOOL bRetVal = FALSE;
switch( dwCtrlType )
{
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
bRetVal = SetEvent( g_hEventBreak );
break;
default:
_ASSERTE( FALSE ); // Some new control code
break;
}
return bRetVal;
}
////////////////////////////////////////////////////////////////////////////
VOID PrintUsage( VOID )
{
_tprintf( _T( "\n" ) );
_tprintf( _T( " Dumps file to another location reading" )
_T( " its sectors directly from disk.\n" ) );
_tprintf( _T( "\n" ) );
_tprintf( _T( " FDUMP [/V] source destination\n" ) );
_tprintf( _T( "\n" ) );
_tprintf( _T( " source Specifies the file to be dumped directly from disk.\n" ) );
_tprintf( _T( " destination Specifies the output file name for the dumped data.\n" ) );
_tprintf( _T( " /V Produce verbose output.\n" ) );
_tprintf( _T( " \n" ) );
}
VOID PrintLine( IN LPCTSTR pszName,
IN LPCTSTR pszValFmt,
IN INT nLineMax,
IN ... )
{
INT i;
va_list vaArgList;
LPTSTR pTmp = NULL;
TCHAR szText[ 256 ] = { 0 };
_tprintf( _T( " %s" ), pszName );
for( i = 0; i < nLineMax - lstrlen( pszName ) + 2; i++ )
_tprintf( _T( "." ) );
va_start( vaArgList, nLineMax );
StringCbVPrintf( szText, sizeof( szText ), pszValFmt, vaArgList );
pTmp = szText + ( g_scrRect.X - nLineMax - 10 ); // :)
for( i = 0; *pTmp++; ++i )
{
*pTmp = _T( '.' );
if( 3 == i )
{
*pTmp = _T( '\0' );
break;
}
}
_tprintf( _T( "%s\n" ), szText );
va_end( vaArgList );
}
VOID PrintVolInfo( IN PFS_VOLUME_INFO pFsVolInfo )
{
static LPCTSTR spszDrvType[] = {
_T( "Unknown" ),
_T( "No Root Dir" ),
_T( "Removable" ),
_T( "Fixed" ),
_T( "Remote" ),
_T( "CDROM" ),
_T( "RAM Disk" ),
};
static LPCTSTR spszFsType[] = {
_T( "UNKNOWN" ),
_T( "NTFS" ),
_T( "exFAT" ),
_T( "FAT12" ),
_T( "FAT16" ),
_T( "FAT32" ),
_T( "HPFS" ),
_T( "CDFS" ),
};
TCHAR szTemp[ 256 ] = { 0 };
LONGLONG llDiskSize = 0;
_ASSERTE( pFsVolInfo );
if( !pFsVolInfo )
return;
llDiskSize = (LONGLONG)pFsVolInfo->dwTotalNumberOfClusters *
(LONGLONG)pFsVolInfo->dwBytesPerCluster;
StrFormatByteSize64( llDiskSize, szTemp, _countof( szTemp ) );
_tprintf( _T( "\n" ) );
_tprintf( _T( " Source Volume Data:\n" ) );
PrintLine( _T( "Volume Path" ), _T( "%s" ), 25, pFsVolInfo->pszPath );
PrintLine( _T( "Unique Name" ), _T( "%s" ), 25, pFsVolInfo->pszName );
PrintLine( _T( "Volume Label" ), _T( "%s" ), 25, pFsVolInfo->pszLabel );
PrintLine( _T( "Serial Number" ), _T( "%d" ), 25, pFsVolInfo->dwSerialNumber );
PrintLine( _T( "Drive Number" ), _T( "%d" ), 25, pFsVolInfo->iDriveNumber );
PrintLine( _T( "Drive Type" ), _T( "%s" ), 25, spszDrvType[ pFsVolInfo->uDriveType ] );
PrintLine( _T( "Handle" ), _T( "%#0.8X" ), 25, pFsVolInfo->hVolume );
PrintLine( _T( "First Sector Offset" ), _T( "%I64d" ), 25, pFsVolInfo->qwFirstSectorOffset );
PrintLine( _T( "Total Size" ), _T( "%s" ), 25, szTemp );
PrintLine( _T( "Bytes Per Sector" ), _T( "%d" ), 25, pFsVolInfo->dwBytesPerSector );
PrintLine( _T( "Sector Per Cluster" ), _T( "%d" ), 25, pFsVolInfo->dwSectorPerCluster );
PrintLine( _T( "Number Of Free Clusters" ), _T( "%d" ), 25, pFsVolInfo->dwNumberOfFreeClusters );
PrintLine( _T( "Number Of Used Cluster" ), _T( "%d" ), 25, pFsVolInfo->dwNumberOfUsedClusters );
PrintLine( _T( "File System Type" ), _T( "%s" ), 25, spszFsType[ pFsVolInfo->dwFileSystemType ] );
PrintLine( _T( "File System Flags" ), _T( "%#0.8X" ), 25, pFsVolInfo->dwFileSystemFlags );
_tprintf( _T( "\n" ) );
}
VOID PrintFileInfo( IN PFS_FILE_INFO pFsFileInfo )
{
TCHAR szTemp[ 256 ] = { 0 };
_ASSERTE( pFsFileInfo );
if( !pFsFileInfo )
return;
StrFormatByteSize64(
pFsFileInfo->liFileSize.QuadPart, szTemp, _countof( szTemp )
);
_tprintf( _T( "\n" ) );
_tprintf( _T( " Source File Data\n" ) );
PrintLine( _T( "Input Path" ), _T( "%s" ), 25, pFsFileInfo->pszPath );
PrintLine( _T( "Resolved path" ), _T( "%s" ), 25, pFsFileInfo->pszResolvedPath );
PrintLine( _T( "File Handle" ), _T( "%#0.8X" ), 25, pFsFileInfo->hFile );
PrintLine( _T( "File Size" ), _T( "%s" ), 25, szTemp );
PrintLine( _T( "NTFS Record Index" ), _T( "%#0.16I64X" ), 25, pFsFileInfo->liFileIndex );
PrintLine( _T( "File Attributes" ), _T( "%#0.8X" ), 25, pFsFileInfo->dwFileAttributes );
_tprintf( _T( "\n" ) );
}
VOID PrintPercentComplete( IN DWORD dwLcn,
IN DWORD dwPercentComplete,
IN BOOL bVerbose )
{
if( dwPercentComplete )
{
static TCHAR spszProg[] =
{ _T( '-' ), _T( '\\' ), _T( '|' ), _T( '/' ) };
static INT iProg = 0;
ConsoleRewindCursorPos();
if( _countof( spszProg ) == iProg )
iProg = 0;
if( bVerbose )
{
_tprintf(
_T( "[%c] #%d, %d%% completed" ),
spszProg[ iProg++ ], dwLcn, dwPercentComplete
);
}
else
{
_tprintf(
_T( "[%c] %d%% completed" ),
spszProg[ iProg++ ], dwPercentComplete
);
}
if( 100 == dwPercentComplete )
_tprintf( _T( "\n" ) );
}
else
{
if( bVerbose )
_tprintf( _T( " Copying logical cluster" ) );
else
_tprintf( _T( " Copying data" ) );
ConsoleGetCursorPos();
}
}
VOID PrintLastError( IN DWORD dwJobType )
{
static LPCTSTR spszJob[] =
{
_T( "complete unknown operation" ),
_T( "get source volume info" ),
_T( "get source file info" ),
_T( "get source file extents" ),
_T( "get source file info from MFT" ),
_T( "create destination file" ),
_T( "copy file data from MFT" ),
_T( "copy data sectors from disk" ),
};
LPTSTR pszMsgBuf = NULL;
_ASSERTE( dwJobType >= FS_JOB_MIN && dwJobType <= FS_JOB_MAX );
if( FS_JOB_MIN > dwJobType || FS_JOB_MAX < dwJobType )
return;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
0,
(LPTSTR)&pszMsgBuf,
0,
NULL
);
_tprintf(
_T( " Cannot %s!\n Last error[%d]: %s\n" ),
spszJob[ dwJobType ], GetLastError(), pszMsgBuf
);
if( pszMsgBuf )
LocalFree( pszMsgBuf );
}
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_( BOOL ) FsProgressCallback( IN LPCVOID lpUserParam,
IN DWORD dwJobType,
IN BOOL bJobDone,
IN LPCVOID lpJobContext,
IN DWORD dwPercentComplete )
{
BOOL bRetVal = bJobDone;
PUSER_PARAMS pUserParams = (PUSER_PARAMS)lpUserParam;
_ASSERTE( pUserParams );
switch( dwJobType )
{
case FS_JOB_GET_SRC_VOLUME_INFO:
{
PFS_VOLUME_INFO pFsVolInfo = (PFS_VOLUME_INFO)lpJobContext;
_ASSERTE( pFsVolInfo );
if( pUserParams->bVerbose )
PrintVolInfo( pFsVolInfo );
if( bJobDone &&
pFsVolInfo->dwFileSystemFlags & FILE_VOLUME_IS_COMPRESSED )
{
_tprintf( _T( "Compressed volumes are not supported!\n" ) );
bRetVal = FALSE;
}
}
break;
case FS_JOB_GET_SRC_FILE_INFO:
{
PFS_FILE_INFO pFsFileInfo = (PFS_FILE_INFO)lpJobContext;
_ASSERTE( pFsFileInfo );
if( pUserParams->bVerbose )
PrintFileInfo( pFsFileInfo );
if( bJobDone &&
( pFsFileInfo->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED ||
pFsFileInfo->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE ) )
{
_tprintf( _T( "Compressed files are not supported!\n" ) );
bRetVal = FALSE;
}
}
break;
case FS_JOB_COPY_DATA_FROM_DISK:
{
PrintPercentComplete(
(DWORD)(DWORD_PTR)lpJobContext, dwPercentComplete, pUserParams->bVerbose
);
}
break;
case FS_JOB_GET_SRC_FILE_EXTENTS:
case FS_JOB_GET_SRC_FILE_MFT:
case FS_JOB_CREATE_FILE_DST:
case FS_JOB_COPY_DATA_FROM_MFT:
break;
default:
_ASSERTE( FALSE ); // Some new job type
return FALSE;
};
if( !bJobDone )
PrintLastError( dwJobType );
return bRetVal;
}
////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_( INT ) ParseCommandLine( IN LPFNPARSEPARAM_CALLBACK lpfnParseParam,
IN LPVOID lpUserParam )
{
INT iArgIndex = 0;
INT iParamIndex = 0;
INT iFlagIndex = 0;
INT iArgCount = 0;
BOOL bCallback = FALSE;
BOOL bArgIsFlag = FALSE;
LPTSTR pszArgument = NULL;
_ASSERTE( !IsBadCodePtr( (FARPROC)lpfnParseParam ) );
if( IsBadCodePtr( (FARPROC)lpfnParseParam ) )
return -1;
iArgCount = __argc;
for( iArgIndex = 1; iArgIndex < iArgCount; iArgIndex++ )
{
pszArgument = __targv[ iArgIndex ];
bArgIsFlag = ( !ChrCmpI( TEXT( '-' ), *pszArgument ) ||
!ChrCmpI( TEXT( '/' ), *pszArgument ) );
if( bArgIsFlag )
++pszArgument;
bCallback = lpfnParseParam(
iArgCount - 1, iArgIndex - 1,
( bArgIsFlag ? iFlagIndex++ : iParamIndex++ ),
pszArgument, bArgIsFlag, lpUserParam
);
if( !bCallback )
break;
}
return ( iArgIndex - 1 );
}
STDAPI_( BOOL ) ParseParamCallback( IN INT iArgCount,
IN INT iArgIndex,
IN INT iArgSubIndex,
IN LPCTSTR pszArgument,
IN BOOL bArgIsFlag,
IN LPVOID lpUserParam )
{
PUSER_PARAMS pUserParams = (PUSER_PARAMS)lpUserParam;
_ASSERTE( pUserParams );
UNREFERENCED_PARAMETER( iArgCount );
if( !pUserParams )
return FALSE;
if( bArgIsFlag )
{
if( !pUserParams->bHelp )
{
pUserParams->bHelp =
!StrCmpI( _T( "?" ), pszArgument ) ||
!StrCmpI( _T( "h" ), pszArgument ) ||
!StrCmpI( _T( "help" ), pszArgument );
}
if( !pUserParams->bVerbose )
{
pUserParams->bVerbose =
!StrCmpI( _T( "v" ), pszArgument ) ||
!StrCmpI( _T( "verbose" ), pszArgument );
}
}
else // if( !bArgIsFlag )
{
LPTSTR pszDstBuf = NULL;
if( 0 == iArgSubIndex )
pszDstBuf = pUserParams->szSrcFile;
if( 1 == iArgSubIndex )
pszDstBuf = pUserParams->szDstFile;
if( pszDstBuf )
StringCchCopy( pszDstBuf, MAX_PATH, pszArgument );
}
if( ( pUserParams->bHelp ) ||
( 3 == iArgCount &&
2 == iArgIndex ) )
{
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////
INT _tmain( IN INT argc, IN TCHAR* argv[] )
{
TCHAR szOldTitle[ MAX_PATH + 1 ] = { 0 };
LPCTSTR pszTitle = _T( "FDUMP version 1.0 by Armen Hakobyan, 2008" );
OSVERSIONINFO osvi = { 0 };
USER_PARAMS userParams = { 0 };
FS_CALLBACK_DATA fsCallbackData = { 0 };
UNREFERENCED_PARAMETER( argc );
UNREFERENCED_PARAMETER( argv );
__try
{
GetConsoleTitle( szOldTitle, _countof( szOldTitle ) );
SetConsoleTitle( pszTitle );
_tprintf( _T( "%s\n" ), pszTitle );
osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
GetVersionEx( &osvi );
if( VER_PLATFORM_WIN32_NT != osvi.dwPlatformId ||
osvi.dwMajorVersion < 5 )
{
_tprintf( _T( "Windows 2000 or later is required!\n" ) );
__leave;
}
if( !IsUserAnAdmin() )
_tprintf( _T( "User must be an administrator to open system volumes!\n" ) );
if( !ParseCommandLine( &ParseParamCallback, &userParams ) ||
userParams.bHelp )
{
PrintUsage();
__leave;
}
if( !lstrlen( userParams.szSrcFile ) ||
!lstrlen( userParams.szDstFile ) )
{
_tprintf( _T( "Invalid source or destination file!\n" ) );
__leave;
}
if( PathIsNetworkPath( userParams.szSrcFile ) )
{
_tprintf( _T( "Source network paths are not supported!\n" ) );
__leave;
}
g_hEventBreak = CreateEvent( NULL, TRUE, FALSE, NULL );
if( !g_hEventBreak )
__leave;
SetConsoleCtrlHandler( &ConsoleCtrlHandler, TRUE );
ConsoleGetCursorPos();
fsCallbackData.hEventBreak = g_hEventBreak;
fsCallbackData.lpUserParam = &userParams;
fsCallbackData.pfnFsProgress = &FsProgressCallback;
FsSectorCopy(
userParams.szSrcFile, userParams.szDstFile, &fsCallbackData
);
SetConsoleTitle( szOldTitle );
}
__finally
{
}
return 0;
}