2025-11-28 00:35:46 +09:00

779 lines
22 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "stdafx.h"
#include "RdcSdkTestServer.h"
#include "RdcFileHandleImpl.h"
#include "globals.h"
static const size_t g_InputBufferSize = 8 * 1024;
static const size_t g_OutputBufferSize = 1024;
/*+---------------------------------------------------------------------------
Class: RdcGeneratorJob
Purpose: Hold the necessary state for generating signatures
during signature generation.
Notes:
----------------------------------------------------------------------------*/
class RdcGeneratorJob
{
public:
RdcGeneratorJob()
:
m_Failed ( false ),
m_Depth ( 0 ),
m_HorizonSize1 ( MSRDC_DEFAULT_HORIZONSIZE_1 ),
m_HorizonSizeN ( MSRDC_DEFAULT_HORIZONSIZE_N ),
m_HashWindowSize1 ( MSRDC_DEFAULT_HASHWINDOWSIZE_1 ),
m_HashWindowSizeN ( MSRDC_DEFAULT_HASHWINDOWSIZE_N ),
m_SimilarityEnabled ( false )
{
for ( ULONG i = 0; i < MSRDC_MAXIMUM_DEPTH; ++i )
{
m_GeneratorParameters[i] = 0;
}
}
~RdcGeneratorJob()
{
for ( ULONG i = 0; i < MSRDC_MAXIMUM_DEPTH; ++i )
{
m_GeneratorParameters[i] = 0;
}
}
DebugHresult AllocateGenerator ( ULONGLONG fileSize, ULONG requestedDepth );
DebugHresult CreateSignatures ( RdcFileHandleImpl *rdcFileHandleImpl );
DebugHresult GetSimilarityData ( SimilarityData * similarityData );
ULONG GetDepth()
{
RDCAssert ( !m_Failed );
return m_Depth;
}
ULONG GetHorizonSize1()
{
RDCAssert ( !m_Failed );
return m_HorizonSize1;
}
DebugHresult SetHorizonSize1 ( ULONG horizonSize )
{
RDCAssert ( !m_Failed );
RDCAssert ( horizonSize >= MSRDC_MINIMUM_HORIZONSIZE );
RDCAssert ( horizonSize <= MSRDC_MAXIMUM_HORIZONSIZE );
if ( horizonSize < MSRDC_MINIMUM_HORIZONSIZE ||
horizonSize > MSRDC_MAXIMUM_HORIZONSIZE )
{
return E_INVALIDARG;
}
m_HorizonSize1 = horizonSize;
return S_OK;
}
ULONG GetHorizonSizeN()
{
RDCAssert ( !m_Failed );
return m_HorizonSizeN;
}
DebugHresult SetHorizonSizeN ( ULONG horizonSize )
{
RDCAssert ( !m_Failed );
RDCAssert ( horizonSize >= MSRDC_MINIMUM_HORIZONSIZE );
RDCAssert ( horizonSize <= MSRDC_MAXIMUM_HORIZONSIZE );
if ( horizonSize < MSRDC_MINIMUM_HORIZONSIZE ||
horizonSize > MSRDC_MAXIMUM_HORIZONSIZE )
{
return E_INVALIDARG;
}
m_HorizonSizeN = horizonSize;
return S_OK;
}
ULONG GetHashWindowSize1()
{
RDCAssert ( !m_Failed );
return m_HashWindowSize1;
}
DebugHresult SetHashWindowSize1 ( ULONG hashWindowSize )
{
RDCAssert ( !m_Failed );
RDCAssert ( hashWindowSize >= MSRDC_MINIMUM_HASHWINDOWSIZE );
RDCAssert ( hashWindowSize <= MSRDC_MAXIMUM_HASHWINDOWSIZE );
if ( hashWindowSize < MSRDC_MINIMUM_HASHWINDOWSIZE ||
hashWindowSize > MSRDC_MAXIMUM_HASHWINDOWSIZE )
{
return E_INVALIDARG;
}
m_HashWindowSize1 = hashWindowSize;
return S_OK;
}
ULONG GetHashWindowSizeN()
{
RDCAssert ( !m_Failed );
return m_HashWindowSizeN;
}
DebugHresult SetHashWindowSizeN ( ULONG hashWindowSize )
{
RDCAssert ( !m_Failed );
RDCAssert ( hashWindowSize >= MSRDC_MINIMUM_HASHWINDOWSIZE );
RDCAssert ( hashWindowSize <= MSRDC_MAXIMUM_HASHWINDOWSIZE );
if ( hashWindowSize < MSRDC_MINIMUM_HASHWINDOWSIZE ||
hashWindowSize > MSRDC_MAXIMUM_HASHWINDOWSIZE )
{
return E_INVALIDARG;
}
m_HashWindowSizeN = hashWindowSize;
return S_OK;
}
private:
bool m_Failed;
ULONG m_Depth;
ULONG m_HorizonSize1;
ULONG m_HorizonSizeN;
ULONG m_HashWindowSize1;
ULONG m_HashWindowSizeN;
// The RDC SDK object
CComQIPtr<IRdcLibrary> m_RdcLibrary;
// The parameters for generation
CComPtr<IRdcGeneratorParameters> m_RdcGeneratorParameters[MSRDC_MAXIMUM_DEPTH];
// The generator
CComPtr<IRdcGenerator> m_RdcGenerator;
CComQIPtr<IRdcSimilarityGenerator> m_RdcSimilarityGenerator;
// Array of pointers to generation parameters
IRdcGeneratorParameters *m_GeneratorParameters[MSRDC_MAXIMUM_DEPTH];
RdcSmartArray<BYTE> m_InputBuffer;
RdcSmartArray<BYTE> m_OutputBuffer[MSRDC_MAXIMUM_DEPTH];
bool m_SimilarityEnabled;
};
/*---------------------------------------------------------------------------
Name: RdcGeneratorJob::AllocateGenerator
Allocate the resources necessary for signature generation.
This includes connecting to the RDC SDK, allocating buffers, and parameters
Arguments:
fileSize - used to compute RDC recursion depth.
requestedDepth - the preferred recursion depth, if 0, a default is calculated
----------------------------------------------------------------------------*/
DebugHresult RdcGeneratorJob::AllocateGenerator (
ULONGLONG fileSize,
ULONG requestedDepth )
{
DebugHresult hr = S_OK;
RDCAssert ( requestedDepth <= MSRDC_MAXIMUM_DEPTH );
RDCAssert ( !m_Failed );
m_Depth = 0;
BYTE *buffer = m_InputBuffer.AppendItems ( g_InputBufferSize );
if ( !buffer )
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED ( hr ) )
{
hr = m_RdcLibrary.CoCreateInstance ( __uuidof ( RdcLibrary ) );
}
if ( SUCCEEDED ( hr ) )
{
if ( requestedDepth == 0 )
{
hr = m_RdcLibrary->ComputeDefaultRecursionDepth ( fileSize, &m_Depth );
}
else
{
m_Depth = requestedDepth;
}
}
if ( SUCCEEDED ( hr ) )
{
if ( m_Depth == 0 )
{
m_Depth = 1;
}
if ( m_Depth > MSRDC_MAXIMUM_DEPTH )
{
m_Depth = MSRDC_MAXIMUM_DEPTH;
}
}
for ( ULONG i = 0; SUCCEEDED ( hr ) && i < m_Depth; ++i )
{
hr = m_RdcLibrary->CreateGeneratorParameters ( RDCGENTYPE_FilterMax, i + 1, &m_RdcGeneratorParameters[i] );
m_GeneratorParameters[i] = m_RdcGeneratorParameters[i];
if ( SUCCEEDED ( hr ) )
{
IRdcGeneratorFilterMaxParameters * q = 0;
if ( i == 0 )
{
hr = m_RdcGeneratorParameters[0].QueryInterface ( &q );
if ( SUCCEEDED ( hr ) )
{
hr = q->SetHashWindowSize ( m_HashWindowSize1 );
}
if ( SUCCEEDED ( hr ) )
{
hr = q->SetHorizonSize ( m_HorizonSize1 );
}
}
else
{
hr = m_RdcGeneratorParameters[i].QueryInterface ( &q );
if ( SUCCEEDED ( hr ) )
{
hr = q->SetHashWindowSize ( m_HashWindowSizeN );
}
if ( SUCCEEDED ( hr ) )
{
hr = q->SetHorizonSize ( m_HorizonSizeN );
}
}
if ( q )
{
q->Release();
}
}
}
if ( SUCCEEDED ( hr ) )
{
hr = m_RdcLibrary->CreateGenerator ( m_Depth, &m_GeneratorParameters[0], &m_RdcGenerator );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_RdcGenerator.QueryInterface ( &m_RdcSimilarityGenerator );
}
if ( SUCCEEDED ( hr ) )
{
m_SimilarityEnabled = true;
hr = m_RdcSimilarityGenerator->EnableSimilarity();
}
for ( ULONG i = 0; SUCCEEDED ( hr ) && i < m_Depth; ++i )
{
if ( !m_OutputBuffer[i].AppendItems ( g_OutputBufferSize ) )
{
hr = E_OUTOFMEMORY;
}
}
if ( FAILED ( hr ) )
{
m_Failed = true;
}
return hr;
}
/*---------------------------------------------------------------------------
Name: RdcGeneratorJob::CreateSignatures
Call the RDC SDK to generate signatures.
Arguments:
rdcFileHandleImpl
Returns:
----------------------------------------------------------------------------*/
DebugHresult RdcGeneratorJob::CreateSignatures ( RdcFileHandleImpl *rdcFileHandleImpl )
{
ULONGLONG totalBytesRead = 0;
RDCAssert ( !m_Failed );
DebugHresult hr = S_OK;
RdcBufferPointer inputPointer =
{
0,
0,
m_InputBuffer.Begin()
};
RdcBufferPointer outputPointer[MSRDC_MAXIMUM_DEPTH];
RdcBufferPointer *outputPointers[MSRDC_MAXIMUM_DEPTH] = {0};
if ( m_Depth > MSRDC_MAXIMUM_DEPTH )
{
return E_FAIL;
}
for ( ULONG i = 0; i < m_Depth; ++i )
{
outputPointer[i].m_Size = static_cast<ULONG> ( m_OutputBuffer[i].Size() );
outputPointer[i].m_Data = m_OutputBuffer[i].Begin();
outputPointer[i].m_Used = 0;
outputPointers[i] = &outputPointer[i];
}
if ( SUCCEEDED ( hr ) )
{
BOOL eof = FALSE;
BOOL eofOutput = FALSE;
do
{
if ( inputPointer.m_Size == inputPointer.m_Used )
{
if ( eof )
{
inputPointer.m_Size = 0;
inputPointer.m_Used = 0;
}
else
{
// When the input buffer is completely empty
// refill it.
DWORD bytesActuallyRead = 0;
DWORD bytesToRead = static_cast<DWORD> ( m_InputBuffer.Size() );
if ( !ReadFile ( rdcFileHandleImpl->GetSourceHandle(),
m_InputBuffer.Begin(),
bytesToRead,
&bytesActuallyRead,
0 ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
else
{
totalBytesRead += bytesActuallyRead;
inputPointer.m_Size = bytesActuallyRead;
inputPointer.m_Used = 0;
if ( bytesActuallyRead < bytesToRead )
{
// Tell RDC there will be no more input.
eof = true;
}
}
}
}
else
{
// Input buffer is not-empty. Give RDC partial
// buffer.
// We could shift the remaining data to the start of the buffer
// and fill up the end - so that we always pass RDC a full buffer,
// but it isn't worth the trouble.
// Typically RDC will consume all the input unless the
// output buffers are really small.
}
RDC_ErrorCode rdc_ErrorCode;
if ( SUCCEEDED ( hr ) )
{
hr = m_RdcGenerator->Process (
eof,
&eofOutput,
&inputPointer,
m_Depth,
outputPointers,
&rdc_ErrorCode );
}
if ( SUCCEEDED ( hr ) )
{
for ( ULONG i = 0; i < m_Depth; ++i )
{
DWORD bytesActuallyWritten = 0;
if ( !WriteFile ( rdcFileHandleImpl->GetSignatureHandle ( i + 1 ),
m_OutputBuffer[i].Begin(),
outputPointers[i]->m_Used,
&bytesActuallyWritten,
0 ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
if ( outputPointers[i]->m_Used != bytesActuallyWritten )
{
hr = E_FAIL;
}
outputPointers[i]->m_Used = 0;
}
}
}
while ( SUCCEEDED ( hr ) && !eofOutput );
}
return hr;
}
DebugHresult RdcGeneratorJob::GetSimilarityData ( SimilarityData * similarityData )
{
DebugHresult hr = S_OK;
if ( m_SimilarityEnabled )
{
hr = m_RdcSimilarityGenerator->Results ( similarityData );
}
else
{
hr = E_FAIL;
}
return hr;
}
RdcFileHandleImpl::~RdcFileHandleImpl()
{
delete m_GeneratorJobInfo;
CloseSignatureFiles();
}
HRESULT RdcFileHandleImpl::OpenSignatureFiles ( FileCreateMode fileCreateMode, DeleteMode deleteMode )
{
DWORD shareMode = FILE_SHARE_READ;
DWORD accessMode = GENERIC_READ;
DWORD openMode = OPEN_EXISTING;
if ( fileCreateMode != ReadOnly )
{
shareMode = FILE_SHARE_DELETE;
accessMode = GENERIC_READ | GENERIC_WRITE;
openMode = CREATE_ALWAYS;
}
HRESULT hr = S_OK;
for ( ULONG i = 0; SUCCEEDED ( hr ) && i < ARRAYSIZE ( m_SignatureFiles ); ++i )
{
wchar_t sourceFileNameSig[MAX_PATH] = {0};
MakeSignatureFileName ( i, sourceFileNameSig );
m_SignatureFiles[i].Set ( CreateFile (
sourceFileNameSig,
accessMode,
shareMode,
0,
openMode,
FILE_ATTRIBUTE_NORMAL,
0 ) );
if ( !m_SignatureFiles[i].IsValid() )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
CloseSignatureFiles();
}
else if ( deleteMode == DeleteOnClose )
{
// Delete the file when it closes
DeleteFile ( sourceFileNameSig );
}
}
return hr;
}
DebugHresult RdcFileHandleImpl::CreateSignatureFiles ( bool &existing, BOOL deleteSigs )
{
CloseSignatureFiles();
DebugHresult hr = S_OK;
// NOTE: We always create new signature files and have them deleted on file
// close. This ensures that we never use out of date signature files.
// A realistic application will likely want to cache signature files and
// have some way to ensure that they get regenerated when the file has changed.
existing = false;
DeleteMode dMode;
if ( deleteSigs )
{
dMode = DeleteOnClose;
}
else
{
dMode = DontDelete;
}
#if 0 // Allow for cached signature files? If enabled, this option doesn't validate validity of the cached files!
HRESULT hr2 = OpenSignatureFiles ( ReadOnly, dMode );
if ( SUCCEEDED ( hr2 ) )
{
existing = true;
}
else
{
existing = false;
hr = OpenSignatureFiles ( CreateNew, dMode );
}
#else
hr = OpenSignatureFiles ( CreateNew, dMode );
#endif
return hr;
}
DebugHresult RdcFileHandleImpl::OpenSource (
const wchar_t *fileName,
RdcFileTransferInfo * fileInfo,
BOOL deleteSigs,
ULONG horizonSize1,
ULONG horizonSizeN,
ULONG hashWindowSize1,
ULONG hashWindowSizeN )
{
DebugHresult hr = S_OK;
if ( m_SourceFile.IsValid() )
{
hr = E_FAIL;
}
if ( SUCCEEDED ( hr ) )
{
wcsncpy_s ( m_SourceFileName, ARRAYSIZE ( m_SourceFileName ), fileName, _TRUNCATE );
m_SourceFileName[ARRAYSIZE ( m_SourceFileName ) - 1] = 0;
m_SourceFile.Set ( CreateFile (
m_SourceFileName,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0 ) );
if ( !m_SourceFile.IsValid() )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
}
if ( SUCCEEDED ( hr ) && !GetFileInformationByHandle ( m_SourceFile.GetHandle(), &m_SourceFileInformation ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
if ( SUCCEEDED ( hr ) )
{
fileInfo->m_FileSize =
( static_cast<ULONGLONG> ( m_SourceFileInformation.nFileSizeHigh ) << 32 ) |
m_SourceFileInformation.nFileSizeLow;
bool existing;
hr = CreateSignatureFiles ( existing, deleteSigs );
if ( !existing )
{
if ( SUCCEEDED ( hr ) )
{
m_GeneratorJobInfo = new ( std::nothrow ) RdcGeneratorJob();
if ( !m_GeneratorJobInfo )
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->SetHorizonSize1 ( horizonSize1 );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->SetHorizonSizeN ( horizonSizeN );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->SetHashWindowSize1 ( hashWindowSize1 );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->SetHashWindowSizeN ( hashWindowSizeN );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->AllocateGenerator ( fileInfo->m_FileSize,
fileInfo->m_SignatureDepth );
}
if ( SUCCEEDED ( hr ) )
{
fileInfo->m_SignatureDepth = m_GeneratorJobInfo->GetDepth();
}
if ( FAILED ( hr ) )
{
delete m_GeneratorJobInfo;
m_GeneratorJobInfo = 0;
}
}
else
{
for ( fileInfo->m_SignatureDepth = 0; fileInfo->m_SignatureDepth < MSRDC_MAXIMUM_DEPTH; ++fileInfo->m_SignatureDepth )
{
if ( !m_SignatureFiles[fileInfo->m_SignatureDepth].IsValid() )
{
break;
}
ULONGLONG fileSize = 0;
hr = GetFileSize ( fileInfo->m_SignatureDepth + 1, &fileSize );
if ( FAILED ( hr ) )
{
break;
}
if ( fileSize == 0 )
{
break;
}
}
}
}
if ( FAILED ( hr ) )
{
m_SourceFile.Close();
CloseSignatureFiles();
}
return hr;
}
DebugHresult RdcFileHandleImpl::GetFileSize (
ULONG signatureLevel,
ULONGLONG *fileSize )
{
DebugHresult hr = S_OK;
HANDLE h = INVALID_HANDLE_VALUE;
*fileSize = 0;
hr = GetHandleForLevel ( signatureLevel, &h );
if ( SUCCEEDED ( hr ) )
{
LARGE_INTEGER position = {0};
if ( !GetFileSizeEx ( h, &position ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
else
{
*fileSize = position.QuadPart;
}
}
return hr;
}
DebugHresult RdcFileHandleImpl::ReadData (
ULONG signatureLevel,
ULONGLONG fileOffset,
ULONG bytesToRead,
ULONG *bytesActuallyRead,
BYTE * data )
{
DebugHresult hr = S_OK;
HANDLE h = INVALID_HANDLE_VALUE;
*bytesActuallyRead = 0;
hr = GetHandleForLevel ( signatureLevel, &h );
if ( SUCCEEDED ( hr ) )
{
LARGE_INTEGER offset;
offset.QuadPart = fileOffset;
if ( !SetFilePointerEx (
h,
offset,
0,
FILE_BEGIN ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
}
}
if ( SUCCEEDED ( hr ) )
{
if ( !ReadFile (
h,
data,
bytesToRead,
bytesActuallyRead,
0 ) )
{
hr = HRESULT_FROM_WIN32 ( GetLastError() );
*bytesActuallyRead = 0;
}
}
return hr;
}
DebugHresult RdcFileHandleImpl::GetHandleForLevel ( ULONG signatureLevel, HANDLE *h )
{
DebugHresult hr = S_OK;
if ( signatureLevel > 0 && m_GeneratorJobInfo )
{
if ( signatureLevel > MSRDC_MAXIMUM_DEPTH ||
!m_SignatureFiles[signatureLevel - 1].IsValid() )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED ( hr ) )
{
// ISSUE: This could be done async...
hr = m_GeneratorJobInfo->CreateSignatures ( this );
}
if ( SUCCEEDED ( hr ) )
{
hr = m_GeneratorJobInfo->GetSimilarityData ( &m_SimilarityData );
if ( SUCCEEDED ( hr ) )
{
m_SimilarityComputed = true;
}
}
delete m_GeneratorJobInfo;
m_GeneratorJobInfo = 0;
}
if ( SUCCEEDED ( hr ) )
{
*h = ( signatureLevel == 0 ) ? m_SourceFile.GetHandle() : m_SignatureFiles[signatureLevel - 1].GetHandle();
}
return hr;
}
DebugHresult RdcFileHandleImpl::GetSimilarityData ( SimilarityData * similarityData )
{
if ( m_SimilarityComputed )
{
memcpy ( similarityData->m_Data, m_SimilarityData.m_Data, ARRAYSIZE ( m_SimilarityData.m_Data ) );
return S_OK;
}
else
{
HANDLE h;
// force signature/traits calculation
DebugHresult hr = GetHandleForLevel ( MSRDC_MINIMUM_DEPTH, &h );
if ( SUCCEEDED ( hr ) )
{
return GetSimilarityData ( similarityData );
}
}
return E_FAIL;
}