You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3802 lines
135 KiB
3802 lines
135 KiB
|
|
#include <precomp.h>
|
|
|
|
//
|
|
// patchapi.c
|
|
//
|
|
// Implementation of PatchAPI for creating and applying patches to files.
|
|
//
|
|
// Author: Tom McGuire (tommcg) 2/97 - 9/97
|
|
//
|
|
// Copyright (C) Microsoft, 1997-1999.
|
|
//
|
|
// MICROSOFT CONFIDENTIAL
|
|
//
|
|
|
|
typedef struct _PATCH_DATA {
|
|
PVOID PatchData;
|
|
ULONG PatchSize;
|
|
} PATCH_DATA, *PPATCH_DATA;
|
|
|
|
//
|
|
// If we're building a DLL, and it's not the applyer-only DLL, we need to
|
|
// hook DLL_PROCESS_DETACH so we can unload imagehlp.dll if we dynamically
|
|
// load it. We only need imagehlp.dll if we're creating patches.
|
|
//
|
|
|
|
#ifdef BUILDING_PATCHAPI_DLL
|
|
#ifndef PATCH_APPLY_CODE_ONLY
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllEntryPoint(
|
|
HANDLE hDll,
|
|
DWORD Reason,
|
|
PVOID Reserved // NULL for dynamic unload, non-NULL for terminating
|
|
)
|
|
{
|
|
if ( Reason == DLL_PROCESS_ATTACH ) {
|
|
DisableThreadLibraryCalls( hDll );
|
|
InitImagehlpCritSect();
|
|
}
|
|
else if (( Reason == DLL_PROCESS_DETACH ) && ( ! Reserved )) {
|
|
UnloadImagehlp();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // ! PATCH_APPLY_CODE_ONLY
|
|
#endif // BUILDING_PATCHAPI_DLL
|
|
|
|
|
|
BOOL
|
|
ProgressCallbackWrapper(
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext,
|
|
IN ULONG CurrentPosition,
|
|
IN ULONG MaximumPosition
|
|
)
|
|
{
|
|
BOOL Success = TRUE;
|
|
|
|
if ( ProgressCallback != NULL ) {
|
|
|
|
__try {
|
|
|
|
Success = ProgressCallback(
|
|
CallbackContext,
|
|
CurrentPosition,
|
|
MaximumPosition
|
|
);
|
|
|
|
if (( ! Success ) && ( GetLastError() == ERROR_SUCCESS )) {
|
|
SetLastError( ERROR_CANCELLED );
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( ERROR_CANCELLED );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPIV
|
|
NormalizeOldFileImageForPatching(
|
|
IN PVOID FileMappedImage,
|
|
IN ULONG FileSize,
|
|
IN ULONG OptionFlags,
|
|
IN PVOID OptionData,
|
|
IN ULONG NewFileCoffBase,
|
|
IN ULONG NewFileCoffTime,
|
|
IN ULONG IgnoreRangeCount,
|
|
IN PPATCH_IGNORE_RANGE IgnoreRangeArray,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
...
|
|
)
|
|
{
|
|
UP_IMAGE_NT_HEADERS32 NtHeader;
|
|
PUCHAR MappedFile;
|
|
BOOL Modified;
|
|
BOOL Success;
|
|
ULONG i;
|
|
|
|
MappedFile = FileMappedImage;
|
|
Modified = FALSE;
|
|
Success = TRUE;
|
|
|
|
__try {
|
|
|
|
NtHeader = GetNtHeader( MappedFile, FileSize );
|
|
|
|
if ( NtHeader ) {
|
|
|
|
//
|
|
// This is a coff image.
|
|
//
|
|
|
|
Modified = NormalizeCoffImage(
|
|
NtHeader,
|
|
MappedFile,
|
|
FileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
NewFileCoffBase,
|
|
NewFileCoffTime
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// Other file type normalizations could be performed here.
|
|
//
|
|
|
|
}
|
|
|
|
#ifdef TESTCODE
|
|
|
|
//
|
|
// The following test-only code creates a file containing
|
|
// the modified coff image to verify that the coff image
|
|
// is really a valid coff image. This is for debugging
|
|
// only.
|
|
//
|
|
|
|
if ( Modified ) {
|
|
|
|
HANDLE hFile = CreateFile(
|
|
"Normalized.out",
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( hFile != INVALID_HANDLE_VALUE ) {
|
|
|
|
DWORD Actual;
|
|
|
|
WriteFile( hFile, MappedFile, FileSize, &Actual, NULL );
|
|
|
|
CloseHandle( hFile );
|
|
|
|
}
|
|
}
|
|
|
|
#endif // TESTCODE
|
|
|
|
for ( i = 0; i < IgnoreRangeCount; i++ ) {
|
|
if (( IgnoreRangeArray[ i ].OffsetInOldFile + IgnoreRangeArray[ i ].LengthInBytes ) <= FileSize ) {
|
|
ZeroMemory( MappedFile + IgnoreRangeArray[ i ].OffsetInOldFile, IgnoreRangeArray[ i ].LengthInBytes );
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
if (( RetainRangeArray[ i ].OffsetInOldFile + RetainRangeArray[ i ].LengthInBytes ) <= FileSize ) {
|
|
ZeroMemory( MappedFile + RetainRangeArray[ i ].OffsetInOldFile, RetainRangeArray[ i ].LengthInBytes );
|
|
}
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
GetFilePatchSignatureA(
|
|
IN LPCSTR FileName,
|
|
IN ULONG OptionFlags,
|
|
IN PVOID OptionData,
|
|
IN ULONG IgnoreRangeCount,
|
|
IN PPATCH_IGNORE_RANGE IgnoreRangeArray,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN ULONG SignatureBufferSize,
|
|
OUT PVOID SignatureBuffer
|
|
)
|
|
{
|
|
BOOL Success = FALSE;
|
|
HANDLE FileHandle;
|
|
|
|
FileHandle = CreateFileA(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = GetFilePatchSignatureByHandle(
|
|
FileHandle,
|
|
OptionFlags,
|
|
OptionData,
|
|
IgnoreRangeCount,
|
|
IgnoreRangeArray,
|
|
RetainRangeCount,
|
|
RetainRangeArray,
|
|
SignatureBufferSize,
|
|
SignatureBuffer
|
|
);
|
|
|
|
CloseHandle( FileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
GetFilePatchSignatureW(
|
|
IN LPCWSTR FileName,
|
|
IN ULONG OptionFlags,
|
|
IN PVOID OptionData,
|
|
IN ULONG IgnoreRangeCount,
|
|
IN PPATCH_IGNORE_RANGE IgnoreRangeArray,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN ULONG SignatureBufferSizeInBytes,
|
|
OUT PVOID SignatureBuffer
|
|
)
|
|
{
|
|
CHAR AnsiSignatureBuffer[ 40 ]; // big enough for hex MD5 (33 bytes)
|
|
HANDLE FileHandle;
|
|
INT Converted;
|
|
BOOL Success = FALSE;
|
|
|
|
FileHandle = CreateFileW(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = GetFilePatchSignatureByHandle(
|
|
FileHandle,
|
|
OptionFlags,
|
|
OptionData,
|
|
IgnoreRangeCount,
|
|
IgnoreRangeArray,
|
|
RetainRangeCount,
|
|
RetainRangeArray,
|
|
sizeof( AnsiSignatureBuffer ),
|
|
AnsiSignatureBuffer
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// Worst case growth from ANSI to UNICODE is 2X.
|
|
//
|
|
|
|
if (( SignatureBufferSizeInBytes / 2 ) < ( strlen( AnsiSignatureBuffer ) + 1 )) {
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
Success = FALSE;
|
|
}
|
|
|
|
else {
|
|
|
|
Converted = MultiByteToWideChar(
|
|
CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
AnsiSignatureBuffer,
|
|
-1,
|
|
SignatureBuffer,
|
|
SignatureBufferSizeInBytes / 2
|
|
);
|
|
|
|
Success = Converted ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
CloseHandle( FileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
GetFilePatchSignatureByHandle(
|
|
IN HANDLE FileHandle,
|
|
IN ULONG OptionFlags,
|
|
IN PVOID OptionData,
|
|
IN ULONG IgnoreRangeCount,
|
|
IN PPATCH_IGNORE_RANGE IgnoreRangeArray,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN ULONG SignatureBufferSize,
|
|
OUT PVOID SignatureBuffer
|
|
)
|
|
{
|
|
PVOID FileMapped;
|
|
ULONG FileSize;
|
|
ULONG FileCrc;
|
|
MD5_HASH FileMD5;
|
|
BOOL Success;
|
|
|
|
Success = MyMapViewOfFileByHandle(
|
|
FileHandle,
|
|
&FileSize,
|
|
&FileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// Note that we must normalize to a fixed known rebase address,
|
|
// so the CRC from this might be different than the OldFileCrc
|
|
// in a patch header that is specific to a new file's rebase
|
|
// address. Note that if PATCH_OPTION_NO_REBASE is specified
|
|
// then the rebase address is ignored.
|
|
//
|
|
|
|
Success = NormalizeOldFileImageForPatching(
|
|
FileMapped,
|
|
FileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
0x10000000, // non-zero fixed coff base
|
|
0x10000000, // non-zero fixed coff time
|
|
IgnoreRangeCount,
|
|
IgnoreRangeArray,
|
|
RetainRangeCount,
|
|
RetainRangeArray
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
if ( OptionFlags & PATCH_OPTION_SIGNATURE_MD5 ) {
|
|
|
|
Success = SafeCompleteMD5(
|
|
FileMapped,
|
|
FileSize,
|
|
&FileMD5
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
if ( SignatureBufferSize < 33 ) {
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
Success = FALSE;
|
|
}
|
|
|
|
else {
|
|
HashToHexString( &FileMD5, ((LPSTR) SignatureBuffer ));
|
|
}
|
|
}
|
|
}
|
|
|
|
else { // signature type is CRC-32
|
|
|
|
Success = SafeCompleteCrc32(
|
|
FileMapped,
|
|
FileSize,
|
|
&FileCrc
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
if ( SignatureBufferSize < 9 ) {
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
Success = FALSE;
|
|
}
|
|
|
|
else {
|
|
DwordToHexString( FileCrc, (LPSTR) SignatureBuffer );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UnmapViewOfFile( FileMapped );
|
|
}
|
|
|
|
if (( ! Success ) &&
|
|
( GetLastError() == ERROR_SUCCESS )) {
|
|
|
|
SetLastError( ERROR_EXTENDED_ERROR );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
#ifndef PATCH_APPLY_CODE_ONLY
|
|
|
|
VOID
|
|
ReduceRiftTable(
|
|
IN PRIFT_TABLE RiftTable
|
|
)
|
|
{
|
|
PRIFT_ENTRY RiftEntryArray = RiftTable->RiftEntryArray;
|
|
PUCHAR RiftUsageArray = RiftTable->RiftUsageArray;
|
|
ULONG RiftEntryCount = RiftTable->RiftEntryCount;
|
|
LONG CurrentDisplacement;
|
|
LONG ThisDisplacement;
|
|
ULONG i;
|
|
|
|
//
|
|
// Essentially we want to remove the usage count from any entry where
|
|
// the preceding USED entry would produce the same rift displacement.
|
|
//
|
|
// The first used entry should contain a non-zero displacement (any
|
|
// USED entries before that should be marked UNUSED because they will
|
|
// coast from zero).
|
|
//
|
|
|
|
CurrentDisplacement = 0;
|
|
|
|
for ( i = 0; i < RiftEntryCount; i++ ) {
|
|
|
|
if ( RiftUsageArray[ i ] != 0 ) {
|
|
|
|
ThisDisplacement = RiftEntryArray[ i ].NewFileRva - RiftEntryArray[ i ].OldFileRva;
|
|
|
|
if ( ThisDisplacement == CurrentDisplacement ) {
|
|
RiftUsageArray[ i ] = 0; // not needed
|
|
}
|
|
else {
|
|
CurrentDisplacement = ThisDisplacement;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // PATCH_APPLY_CODE_ONLY
|
|
|
|
|
|
BOOL
|
|
WINAPIV
|
|
TransformOldFileImageForPatching(
|
|
IN ULONG TransformOptions,
|
|
IN PVOID OldFileMapped,
|
|
IN ULONG OldFileSize,
|
|
IN ULONG NewFileResTime,
|
|
IN PRIFT_TABLE RiftTable,
|
|
...
|
|
)
|
|
{
|
|
UP_IMAGE_NT_HEADERS32 NtHeader;
|
|
BOOL Success = TRUE;
|
|
|
|
__try {
|
|
|
|
NtHeader = GetNtHeader( OldFileMapped, OldFileSize );
|
|
|
|
if ( NtHeader ) {
|
|
|
|
Success = TransformCoffImage(
|
|
TransformOptions,
|
|
NtHeader,
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileResTime,
|
|
RiftTable,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// Other file type transformations could be performed here.
|
|
//
|
|
|
|
}
|
|
|
|
#ifndef PATCH_APPLY_CODE_ONLY
|
|
|
|
if ( RiftTable->RiftUsageArray != NULL ) {
|
|
ReduceRiftTable( RiftTable );
|
|
}
|
|
|
|
#endif // PATCH_APPLY_CODE_ONLY
|
|
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
|
|
#ifdef TESTCODE
|
|
|
|
//
|
|
// The following test-only code creates a file containing
|
|
// the modified coff image to verify that the coff image
|
|
// is really a valid coff image. This is for debugging
|
|
// only.
|
|
//
|
|
|
|
if ( Success ) {
|
|
|
|
HANDLE hFile = CreateFile(
|
|
"Transformed.out",
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( hFile != INVALID_HANDLE_VALUE ) {
|
|
|
|
DWORD Actual;
|
|
|
|
WriteFile( hFile, OldFileMapped, OldFileSize, &Actual, NULL );
|
|
|
|
CloseHandle( hFile );
|
|
|
|
}
|
|
}
|
|
|
|
#endif // TESTCODE
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
PUCHAR
|
|
__fastcall
|
|
VariableLengthUnsignedDecode(
|
|
IN PUCHAR Buffer,
|
|
OUT PULONG ReturnValue
|
|
)
|
|
{
|
|
PUCHAR p = Buffer;
|
|
ULONG Value = 0;
|
|
ULONG Shift = 0;
|
|
|
|
do {
|
|
Value |= (( *p & 0x7F ) << Shift );
|
|
Shift += 7;
|
|
}
|
|
while (( ! ( *p++ & 0x80 )) && ( Shift < 32 ));
|
|
|
|
*ReturnValue = Value;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
PUCHAR
|
|
__fastcall
|
|
VariableLengthSignedDecode(
|
|
IN PUCHAR Buffer,
|
|
OUT PLONG ReturnValue
|
|
)
|
|
{
|
|
PUCHAR p = Buffer;
|
|
ULONG Shift;
|
|
LONG Value;
|
|
|
|
Value = *p & 0x3F;
|
|
Shift = 6;
|
|
|
|
if ( ! ( *p++ & 0x80 )) {
|
|
do {
|
|
Value |= (( *p & 0x7F ) << Shift );
|
|
Shift += 7;
|
|
}
|
|
while (( ! ( *p++ & 0x80 )) && ( Shift < 32 ));
|
|
}
|
|
|
|
if ( *Buffer & 0x40 ) {
|
|
Value = -Value;
|
|
}
|
|
|
|
*ReturnValue = Value;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
UCHAR
|
|
PatchVersion(
|
|
IN ULONG PatchSignature
|
|
)
|
|
{
|
|
union {
|
|
ULONG Signature;
|
|
UCHAR Byte[ 4 ];
|
|
} u;
|
|
|
|
u.Signature = PatchSignature;
|
|
|
|
if (( u.Byte[ 0 ] == 'P' ) && ( u.Byte[ 1 ] == 'A' ) &&
|
|
( u.Byte[ 2 ] >= '0' ) && ( u.Byte[ 2 ] <= '9' ) &&
|
|
( u.Byte[ 3 ] >= '0' ) && ( u.Byte[ 3 ] <= '9' )) {
|
|
|
|
return (UCHAR)(( u.Byte[ 2 ] - '0' ) * 10 + ( u.Byte[ 3 ] - '0' ));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DecodePatchHeader(
|
|
IN PVOID PatchHeader,
|
|
IN ULONG PatchHeaderMaxSize,
|
|
IN HANDLE SubAllocator,
|
|
OUT PULONG PatchHeaderActualSize,
|
|
OUT PPATCH_HEADER_INFO *HeaderInfo
|
|
)
|
|
{
|
|
PHEADER_OLD_FILE_INFO OldFileInfo;
|
|
PPATCH_HEADER_INFO Header;
|
|
ULONG i, j;
|
|
LONG Delta;
|
|
LONG DeltaNew;
|
|
ULONG DeltaPos;
|
|
ULONG Length;
|
|
ULONG PreviousOffset;
|
|
ULONG PreviousOldRva;
|
|
ULONG PreviousNewRva;
|
|
BOOL Success;
|
|
PUCHAR p;
|
|
|
|
//
|
|
// A couple of implementation notes here. The PatchHeaderMaxSize
|
|
// value does NOT guarantee that we won't try to read beyond that
|
|
// memory address in this routine. This routine should be called
|
|
// under try/except to trap the case where we walk off the end of
|
|
// a corrupt patch header. The PatchHeaderMaxSize is just a helper
|
|
// value that lets us know if we did have a corrupt header in the
|
|
// case where we walked too far but not off the end of the page.
|
|
//
|
|
|
|
Success = FALSE;
|
|
|
|
p = PatchHeader;
|
|
|
|
Header = SubAllocate( SubAllocator, sizeof( PATCH_HEADER_INFO ));
|
|
|
|
//
|
|
// SubAllocate provides zeroed memory.
|
|
//
|
|
|
|
if ( Header != NULL ) {
|
|
|
|
__try {
|
|
|
|
Header->Signature = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
|
|
if ( Header->Signature != PATCH_SIGNATURE ) {
|
|
if ( PatchVersion( Header->Signature ) > PatchVersion( PATCH_SIGNATURE )) {
|
|
SetLastError( ERROR_PATCH_NEWER_FORMAT );
|
|
}
|
|
else {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
}
|
|
__leave;
|
|
}
|
|
|
|
Header->OptionFlags = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
|
|
//
|
|
// The PATCH_OPTION_NO_TIMESTAMP flag is stored inverse for
|
|
// backward compatibility, so flip it back here.
|
|
//
|
|
|
|
Header->OptionFlags ^= PATCH_OPTION_NO_TIMESTAMP;
|
|
|
|
//
|
|
// Now check for invalid flags.
|
|
//
|
|
|
|
if ( Header->OptionFlags & ~PATCH_OPTION_VALID_FLAGS ) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If the PATCH_OPTION_EXTENDED_OPTIONS flag is set, the next
|
|
// 4 bytes is the ExtendedOptionFlags value.
|
|
//
|
|
|
|
if ( Header->OptionFlags & PATCH_OPTION_EXTENDED_OPTIONS ) {
|
|
|
|
Header->ExtendedOptionFlags = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
}
|
|
|
|
//
|
|
// No stored OptionData defined for now.
|
|
//
|
|
|
|
if ( ! ( Header->OptionFlags & PATCH_OPTION_NO_TIMESTAMP )) {
|
|
|
|
Header->NewFileTime = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
}
|
|
|
|
if ( ! ( Header->OptionFlags & PATCH_OPTION_NO_REBASE )) {
|
|
|
|
Header->NewFileCoffBase = ((ULONG)*(UNALIGNED USHORT *)( p )) << 16;
|
|
p += sizeof( USHORT );
|
|
|
|
ASSERT( Header->NewFileCoffBase != 0 );
|
|
|
|
//
|
|
// If NewFileTime is nonzero, CoffTime is stored as a signed
|
|
// delta from NewFileTime since they are usually very close.
|
|
// If NewFileTime is zero, CoffTime is encoded as a ULONG.
|
|
//
|
|
|
|
if ( Header->NewFileTime != 0 ) {
|
|
|
|
p = VariableLengthSignedDecode( p, &Delta );
|
|
Header->NewFileCoffTime = Header->NewFileTime - Delta;
|
|
}
|
|
|
|
else {
|
|
|
|
Header->NewFileCoffTime = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
}
|
|
}
|
|
|
|
if ( ! ( Header->OptionFlags & PATCH_OPTION_NO_RESTIMEFIX )) {
|
|
|
|
//
|
|
// If NewFileCoffTime is nonzero, ResTime is stored as a
|
|
// signed delta from NewFileCoffTime since they are usually
|
|
// very close. If NewFileCoffTime is zero, ResTime is
|
|
// encoded as a ULONG.
|
|
//
|
|
|
|
if ( Header->NewFileCoffTime != 0 ) {
|
|
|
|
p = VariableLengthSignedDecode( p, &Delta );
|
|
Header->NewFileResTime = Header->NewFileCoffTime - Delta;
|
|
}
|
|
|
|
else {
|
|
|
|
Header->NewFileResTime = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
}
|
|
}
|
|
|
|
p = VariableLengthUnsignedDecode( p, &Header->NewFileSize );
|
|
|
|
Header->NewFileCrc = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
|
|
Header->OldFileCount = *p++;
|
|
|
|
Header->OldFileInfoArray = SubAllocate( SubAllocator, Header->OldFileCount * sizeof( HEADER_OLD_FILE_INFO ));
|
|
|
|
if ( Header->OldFileInfoArray == NULL ) {
|
|
__leave;
|
|
}
|
|
|
|
for ( i = 0; i < Header->OldFileCount; i++ ) {
|
|
|
|
OldFileInfo = &Header->OldFileInfoArray[ i ];
|
|
|
|
p = VariableLengthSignedDecode( p, &Delta );
|
|
|
|
if ((LONG)( Header->NewFileSize + Delta ) < 0 ) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
__leave;
|
|
}
|
|
|
|
OldFileInfo->OldFileSize = Header->NewFileSize + Delta;
|
|
|
|
OldFileInfo->OldFileCrc = *(UNALIGNED ULONG *)( p );
|
|
p += sizeof( ULONG );
|
|
|
|
OldFileInfo->IgnoreRangeCount = *p++;
|
|
|
|
if ( OldFileInfo->IgnoreRangeCount != 0 ) {
|
|
|
|
OldFileInfo->IgnoreRangeArray = SubAllocate( SubAllocator, OldFileInfo->IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE ));
|
|
|
|
if ( OldFileInfo->IgnoreRangeArray == NULL ) {
|
|
__leave;
|
|
}
|
|
|
|
PreviousOffset = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->IgnoreRangeCount; j++ ) {
|
|
|
|
p = VariableLengthSignedDecode( p, &Delta );
|
|
p = VariableLengthUnsignedDecode( p, &Length );
|
|
|
|
OldFileInfo->IgnoreRangeArray[ j ].OffsetInOldFile = PreviousOffset + Delta;
|
|
OldFileInfo->IgnoreRangeArray[ j ].LengthInBytes = Length;
|
|
|
|
PreviousOffset = PreviousOffset + Delta + Length;
|
|
|
|
if ( PreviousOffset > OldFileInfo->OldFileSize ) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
OldFileInfo->RetainRangeCount = *p++;
|
|
|
|
if ( OldFileInfo->RetainRangeCount != 0 ) {
|
|
|
|
OldFileInfo->RetainRangeArray = SubAllocate( SubAllocator, OldFileInfo->RetainRangeCount * sizeof( PATCH_RETAIN_RANGE ));
|
|
|
|
if ( OldFileInfo->RetainRangeArray == NULL ) {
|
|
__leave;
|
|
}
|
|
|
|
PreviousOffset = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->RetainRangeCount; j++ ) {
|
|
|
|
p = VariableLengthSignedDecode( p, &Delta );
|
|
p = VariableLengthSignedDecode( p, &DeltaNew );
|
|
p = VariableLengthUnsignedDecode( p, &Length );
|
|
|
|
OldFileInfo->RetainRangeArray[ j ].OffsetInOldFile = PreviousOffset + Delta;
|
|
OldFileInfo->RetainRangeArray[ j ].OffsetInNewFile = PreviousOffset + Delta + DeltaNew;
|
|
OldFileInfo->RetainRangeArray[ j ].LengthInBytes = Length;
|
|
|
|
PreviousOffset = PreviousOffset + Delta + Length;
|
|
|
|
if (( PreviousOffset > OldFileInfo->OldFileSize ) ||
|
|
(( PreviousOffset + DeltaNew ) > Header->NewFileSize )) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
p = VariableLengthUnsignedDecode( p, &OldFileInfo->RiftTable.RiftEntryCount );
|
|
|
|
if ( OldFileInfo->RiftTable.RiftEntryCount != 0 ) {
|
|
|
|
OldFileInfo->RiftTable.RiftEntryArray = SubAllocate( SubAllocator, OldFileInfo->RiftTable.RiftEntryCount * sizeof( RIFT_ENTRY ));
|
|
|
|
if ( OldFileInfo->RiftTable.RiftEntryArray == NULL ) {
|
|
__leave;
|
|
}
|
|
|
|
OldFileInfo->RiftTable.RiftUsageArray = NULL;
|
|
|
|
PreviousOldRva = 0;
|
|
PreviousNewRva = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->RiftTable.RiftEntryCount; j++ ) {
|
|
|
|
p = VariableLengthUnsignedDecode( p, &DeltaPos );
|
|
p = VariableLengthSignedDecode( p, &DeltaNew );
|
|
|
|
OldFileInfo->RiftTable.RiftEntryArray[ j ].OldFileRva = PreviousOldRva + DeltaPos;
|
|
OldFileInfo->RiftTable.RiftEntryArray[ j ].NewFileRva = PreviousNewRva + DeltaNew;
|
|
|
|
PreviousOldRva += DeltaPos;
|
|
PreviousNewRva += DeltaNew;
|
|
}
|
|
}
|
|
|
|
p = VariableLengthUnsignedDecode( p, &OldFileInfo->PatchDataSize );
|
|
}
|
|
|
|
if ( p > ((PUCHAR)PatchHeader + PatchHeaderMaxSize )) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
__leave;
|
|
}
|
|
|
|
Success = TRUE;
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
if ( PatchHeaderActualSize ) {
|
|
*PatchHeaderActualSize = (ULONG)( p - (PUCHAR)PatchHeader );
|
|
}
|
|
|
|
if ( HeaderInfo ) {
|
|
*HeaderInfo = Header;
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
//
|
|
// Following group of functions and exported apis are exclusively for
|
|
// creating patches. If we're only compiling the apply code, ignore
|
|
// this group of functions.
|
|
//
|
|
|
|
#ifndef PATCH_APPLY_CODE_ONLY
|
|
|
|
PUCHAR
|
|
__fastcall
|
|
VariableLengthUnsignedEncode(
|
|
OUT PUCHAR Buffer,
|
|
IN ULONG Value
|
|
)
|
|
{
|
|
UCHAR Byte = (UCHAR)( Value & 0x7F ); // low order 7 bits
|
|
|
|
Value >>= 7;
|
|
|
|
while ( Value ) {
|
|
|
|
*Buffer++ = Byte;
|
|
|
|
Byte = (UCHAR)( Value & 0x7F ); // next 7 higher order bits
|
|
|
|
Value >>= 7;
|
|
|
|
}
|
|
|
|
*Buffer++ = (UCHAR)( Byte | 0x80 );
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
PUCHAR
|
|
__fastcall
|
|
VariableLengthSignedEncode(
|
|
OUT PUCHAR Buffer,
|
|
IN LONG Value
|
|
)
|
|
{
|
|
UCHAR Byte;
|
|
|
|
if ( Value < 0 ) {
|
|
Value = -Value;
|
|
Byte = (UCHAR)(( Value & 0x3F ) | 0x40 );
|
|
}
|
|
else {
|
|
Byte = (UCHAR)( Value & 0x3F );
|
|
}
|
|
|
|
Value >>= 6;
|
|
|
|
while ( Value ) {
|
|
|
|
*Buffer++ = Byte;
|
|
|
|
Byte = (UCHAR)( Value & 0x7F ); // next 7 higher order bits
|
|
|
|
Value >>= 7;
|
|
|
|
}
|
|
|
|
*Buffer++ = (UCHAR)( Byte | 0x80 );
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
ULONG
|
|
EncodePatchHeader(
|
|
IN PPATCH_HEADER_INFO HeaderInfo,
|
|
OUT PVOID PatchHeaderBuffer
|
|
)
|
|
{
|
|
PHEADER_OLD_FILE_INFO OldFileInfo;
|
|
ULONG i, j;
|
|
LONG Delta;
|
|
ULONG PreviousOffset;
|
|
ULONG PreviousOldRva;
|
|
ULONG PreviousNewRva;
|
|
ULONG ActiveRiftCount;
|
|
|
|
PUCHAR p = PatchHeaderBuffer;
|
|
|
|
#ifdef TESTCODE
|
|
PUCHAR q;
|
|
#endif // TESTCODE
|
|
|
|
ASSERT( HeaderInfo->Signature == PATCH_SIGNATURE );
|
|
ASSERT((( HeaderInfo->OptionFlags & ~PATCH_OPTION_VALID_FLAGS ) == 0 ));
|
|
ASSERT((( HeaderInfo->OptionFlags & PATCH_OPTION_EXTENDED_OPTIONS ) != 0 ) == ( HeaderInfo->ExtendedOptionFlags != 0 ));
|
|
ASSERT((( HeaderInfo->OptionFlags & PATCH_OPTION_NO_TIMESTAMP ) == 0 ) == ( HeaderInfo->NewFileTime != 0 ));
|
|
ASSERT((( HeaderInfo->OptionFlags & PATCH_OPTION_NO_REBASE ) == 0 ) == ( HeaderInfo->NewFileCoffBase != 0 ));
|
|
ASSERT((( HeaderInfo->OptionFlags & PATCH_OPTION_NO_REBASE ) == 0 ) == ( HeaderInfo->NewFileCoffTime != 0 ));
|
|
ASSERT((( HeaderInfo->OptionFlags & PATCH_OPTION_NO_RESTIMEFIX ) == 0 ) == ( HeaderInfo->NewFileResTime != 0 ));
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->Signature;
|
|
p += sizeof( ULONG );
|
|
|
|
//
|
|
// The PATCH_OPTION_NO_TIMESTAMP flag is stored inverse for
|
|
// backward compatibility, so flip it when storing it here.
|
|
//
|
|
|
|
*(UNALIGNED ULONG *)( p ) = ( HeaderInfo->OptionFlags ^ PATCH_OPTION_NO_TIMESTAMP );
|
|
p += sizeof( ULONG );
|
|
|
|
//
|
|
// If the PATCH_OPTION_EXTENDED_OPTIONS flag is set, the next
|
|
// 4 bytes is the ExtendedOptionFlags value.
|
|
//
|
|
|
|
if ( HeaderInfo->OptionFlags & PATCH_OPTION_EXTENDED_OPTIONS ) {
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->ExtendedOptionFlags;
|
|
p += sizeof( ULONG );
|
|
}
|
|
|
|
//
|
|
// No stored OptionData defined for now.
|
|
//
|
|
|
|
if ( ! ( HeaderInfo->OptionFlags & PATCH_OPTION_NO_TIMESTAMP )) {
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->NewFileTime;
|
|
p += sizeof( ULONG );
|
|
}
|
|
|
|
if ( ! ( HeaderInfo->OptionFlags & PATCH_OPTION_NO_REBASE )) {
|
|
|
|
ASSERT(( HeaderInfo->NewFileCoffBase >> 16 ) != 0 );
|
|
|
|
*(UNALIGNED USHORT *)( p ) = (USHORT)( HeaderInfo->NewFileCoffBase >> 16 );
|
|
p += sizeof( USHORT );
|
|
|
|
//
|
|
// If NewFileTime is nonzero, CoffTime is stored as a signed
|
|
// delta from NewFileTime since they are usually very close.
|
|
// If NewFileTime is zero, CoffTime is encoded as a ULONG.
|
|
//
|
|
|
|
if ( HeaderInfo->NewFileTime != 0 ) {
|
|
|
|
Delta = HeaderInfo->NewFileTime - HeaderInfo->NewFileCoffTime;
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
}
|
|
|
|
else {
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->NewFileCoffTime;
|
|
p += sizeof( ULONG );
|
|
}
|
|
}
|
|
|
|
if ( ! ( HeaderInfo->OptionFlags & PATCH_OPTION_NO_RESTIMEFIX )) {
|
|
|
|
//
|
|
// If NewFileCoffTime is nonzero, ResTime is stored as a
|
|
// signed delta from NewFileCoffTime since they are usually
|
|
// very close. If NewFileCoffTime is zero, ResTime is
|
|
// encoded as a ULONG.
|
|
//
|
|
|
|
if ( HeaderInfo->NewFileCoffTime != 0 ) {
|
|
|
|
Delta = HeaderInfo->NewFileCoffTime - HeaderInfo->NewFileResTime;
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
}
|
|
|
|
else {
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->NewFileResTime;
|
|
p += sizeof( ULONG );
|
|
}
|
|
}
|
|
|
|
p = VariableLengthUnsignedEncode( p, HeaderInfo->NewFileSize );
|
|
|
|
*(UNALIGNED ULONG *)( p ) = HeaderInfo->NewFileCrc;
|
|
p += sizeof( ULONG );
|
|
|
|
ASSERT( HeaderInfo->OldFileCount < 256 );
|
|
|
|
*p++ = (UCHAR)( HeaderInfo->OldFileCount );
|
|
|
|
for ( i = 0; i < HeaderInfo->OldFileCount; i++ ) {
|
|
|
|
OldFileInfo = &HeaderInfo->OldFileInfoArray[ i ];
|
|
|
|
Delta = OldFileInfo->OldFileSize - HeaderInfo->NewFileSize;
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
|
|
*(UNALIGNED ULONG *)( p ) = OldFileInfo->OldFileCrc;
|
|
p += sizeof( ULONG );
|
|
|
|
ASSERT( OldFileInfo->IgnoreRangeCount < 256 );
|
|
|
|
*p++ = (UCHAR)( OldFileInfo->IgnoreRangeCount );
|
|
|
|
PreviousOffset = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->IgnoreRangeCount; j++ ) {
|
|
|
|
Delta = OldFileInfo->IgnoreRangeArray[ j ].OffsetInOldFile - PreviousOffset;
|
|
|
|
PreviousOffset = OldFileInfo->IgnoreRangeArray[ j ].OffsetInOldFile +
|
|
OldFileInfo->IgnoreRangeArray[ j ].LengthInBytes;
|
|
|
|
ASSERT( PreviousOffset <= OldFileInfo->OldFileSize );
|
|
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
|
|
p = VariableLengthUnsignedEncode( p, OldFileInfo->IgnoreRangeArray[ j ].LengthInBytes );
|
|
}
|
|
|
|
ASSERT( OldFileInfo->RetainRangeCount < 256 );
|
|
|
|
*p++ = (UCHAR)( OldFileInfo->RetainRangeCount );
|
|
|
|
PreviousOffset = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->RetainRangeCount; j++ ) {
|
|
|
|
Delta = OldFileInfo->RetainRangeArray[ j ].OffsetInOldFile - PreviousOffset;
|
|
|
|
PreviousOffset = OldFileInfo->RetainRangeArray[ j ].OffsetInOldFile +
|
|
OldFileInfo->RetainRangeArray[ j ].LengthInBytes;
|
|
|
|
ASSERT( PreviousOffset <= OldFileInfo->OldFileSize );
|
|
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
|
|
Delta = OldFileInfo->RetainRangeArray[ j ].OffsetInNewFile -
|
|
OldFileInfo->RetainRangeArray[ j ].OffsetInOldFile;
|
|
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
|
|
p = VariableLengthUnsignedEncode( p, OldFileInfo->RetainRangeArray[ j ].LengthInBytes );
|
|
}
|
|
|
|
ActiveRiftCount = 0;
|
|
|
|
ASSERT(( OldFileInfo->RiftTable.RiftEntryCount == 0 ) || ( OldFileInfo->RiftTable.RiftUsageArray != NULL ));
|
|
|
|
for ( j = 0; j < OldFileInfo->RiftTable.RiftEntryCount; j++ ) {
|
|
if ( OldFileInfo->RiftTable.RiftUsageArray[ j ] ) {
|
|
++ActiveRiftCount;
|
|
}
|
|
}
|
|
|
|
#ifdef TESTCODE2
|
|
|
|
fprintf( stderr, "\n\n" );
|
|
|
|
#endif // TESTCODE2
|
|
|
|
#ifdef TESTCODE
|
|
|
|
q = p;
|
|
|
|
#endif // TESTCODE
|
|
|
|
if (( OldFileInfo->RiftTable.RiftEntryCount ) && ( ActiveRiftCount == 0 )) {
|
|
|
|
//
|
|
// This is a special case. We have a rift table but didn't use
|
|
// any entries during transformation. This can happen if all the
|
|
// rifts coast to zero for extremely similar files. If we encode
|
|
// the rift count as zero, no transformations will be performed
|
|
// during patch apply. To prevent that, we'll encode one rift of
|
|
// 0,0 which is usually just the implied initial rift.
|
|
//
|
|
|
|
ActiveRiftCount = 1;
|
|
|
|
p = VariableLengthUnsignedEncode( p, ActiveRiftCount );
|
|
p = VariableLengthUnsignedEncode( p, 0 );
|
|
p = VariableLengthSignedEncode( p, 0 );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p = VariableLengthUnsignedEncode( p, ActiveRiftCount );
|
|
|
|
PreviousOldRva = 0;
|
|
PreviousNewRva = 0;
|
|
|
|
for ( j = 0; j < OldFileInfo->RiftTable.RiftEntryCount; j++ ) {
|
|
|
|
if ( OldFileInfo->RiftTable.RiftUsageArray[ j ] ) {
|
|
|
|
#ifdef TESTCODE2
|
|
fprintf( stderr, "%9X ", OldFileInfo->RiftTable.RiftEntryArray[ j ].OldFileRva );
|
|
fprintf( stderr, "%9X ", OldFileInfo->RiftTable.RiftEntryArray[ j ].NewFileRva );
|
|
#endif // TESTCODE2
|
|
|
|
Delta = OldFileInfo->RiftTable.RiftEntryArray[ j ].OldFileRva - PreviousOldRva;
|
|
|
|
ASSERT( Delta > 0 ); // sorted by OldFileRva
|
|
|
|
#ifdef TESTCODE2
|
|
fprintf( stderr, "%9d ", Delta );
|
|
|
|
#endif // TESTCODE2
|
|
|
|
PreviousOldRva = OldFileInfo->RiftTable.RiftEntryArray[ j ].OldFileRva;
|
|
|
|
p = VariableLengthUnsignedEncode( p, Delta );
|
|
|
|
Delta = OldFileInfo->RiftTable.RiftEntryArray[ j ].NewFileRva - PreviousNewRva;
|
|
|
|
#ifdef TESTCODE2
|
|
fprintf( stderr, "%9d\n", Delta );
|
|
#endif // TESTCODE2
|
|
|
|
PreviousNewRva = OldFileInfo->RiftTable.RiftEntryArray[ j ].NewFileRva;
|
|
|
|
p = VariableLengthSignedEncode( p, Delta );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TESTCODE
|
|
|
|
if ( ActiveRiftCount > 0 ) {
|
|
printf( "\r%9d rifts encoded in %d bytes (%.1f bytes per rift)\n", ActiveRiftCount, p - q, ((double)( p - q ) / ActiveRiftCount ));
|
|
}
|
|
|
|
#endif // TESTCODE
|
|
|
|
p = VariableLengthUnsignedEncode( p, OldFileInfo->PatchDataSize );
|
|
}
|
|
|
|
return (ULONG)( p - (PUCHAR) PatchHeaderBuffer );
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileA(
|
|
IN LPCSTR OldFileName,
|
|
IN LPCSTR NewFileName,
|
|
OUT LPCSTR PatchFileName,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData // optional
|
|
)
|
|
{
|
|
PATCH_OLD_FILE_INFO_A OldFileInfo = {
|
|
sizeof( PATCH_OLD_FILE_INFO_A ),
|
|
OldFileName,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
};
|
|
|
|
return CreatePatchFileExA(
|
|
1,
|
|
&OldFileInfo,
|
|
NewFileName,
|
|
PatchFileName,
|
|
OptionFlags,
|
|
OptionData,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileW(
|
|
IN LPCWSTR OldFileName,
|
|
IN LPCWSTR NewFileName,
|
|
OUT LPCWSTR PatchFileName,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData // optional
|
|
)
|
|
{
|
|
PATCH_OLD_FILE_INFO_W OldFileInfo = {
|
|
sizeof( PATCH_OLD_FILE_INFO_W ),
|
|
OldFileName,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
};
|
|
|
|
return CreatePatchFileExW(
|
|
1,
|
|
&OldFileInfo,
|
|
NewFileName,
|
|
PatchFileName,
|
|
OptionFlags,
|
|
OptionData,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileByHandles(
|
|
IN HANDLE OldFileHandle,
|
|
IN HANDLE NewFileHandle,
|
|
OUT HANDLE PatchFileHandle,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData // optional
|
|
)
|
|
{
|
|
PATCH_OLD_FILE_INFO_H OldFileInfo = {
|
|
sizeof( PATCH_OLD_FILE_INFO_H ),
|
|
OldFileHandle,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
};
|
|
|
|
return CreatePatchFileByHandlesEx(
|
|
1,
|
|
&OldFileInfo,
|
|
NewFileHandle,
|
|
PatchFileHandle,
|
|
OptionFlags,
|
|
OptionData,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileExA(
|
|
IN ULONG OldFileCount,
|
|
IN PPATCH_OLD_FILE_INFO_A OldFileInfoArray,
|
|
IN LPCSTR NewFileName,
|
|
OUT LPCSTR PatchFileName,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData, // optional
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
PPATCH_OLD_FILE_INFO_H OldFileInfoByHandle = NULL;
|
|
HANDLE PatchFileHandle;
|
|
HANDLE NewFileHandle;
|
|
BOOL Success;
|
|
ULONG i;
|
|
|
|
if ( OldFileCount == 0 ) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
OldFileInfoByHandle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, OldFileCount * sizeof(PATCH_OLD_FILE_INFO_H));
|
|
if (!OldFileInfoByHandle) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
Success = TRUE;
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
|
|
OldFileInfoByHandle[ i ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_H );
|
|
OldFileInfoByHandle[ i ].IgnoreRangeCount = OldFileInfoArray[ i ].IgnoreRangeCount;
|
|
OldFileInfoByHandle[ i ].IgnoreRangeArray = OldFileInfoArray[ i ].IgnoreRangeArray;
|
|
OldFileInfoByHandle[ i ].RetainRangeCount = OldFileInfoArray[ i ].RetainRangeCount;
|
|
OldFileInfoByHandle[ i ].RetainRangeArray = OldFileInfoArray[ i ].RetainRangeArray;
|
|
|
|
OldFileInfoByHandle[ i ].OldFileHandle = CreateFileA(
|
|
OldFileInfoArray[ i ].OldFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( OldFileInfoByHandle[ i ].OldFileHandle == INVALID_HANDLE_VALUE ) {
|
|
Success = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
Success = FALSE;
|
|
|
|
NewFileHandle = CreateFileA(
|
|
NewFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( NewFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
PatchFileHandle = CreateFileA(
|
|
PatchFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = CreatePatchFileByHandlesEx(
|
|
OldFileCount,
|
|
OldFileInfoByHandle,
|
|
NewFileHandle,
|
|
PatchFileHandle,
|
|
OptionFlags,
|
|
OptionData,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileA( PatchFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( NewFileHandle );
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
if (( OldFileInfoByHandle[ i ].OldFileHandle != NULL ) &&
|
|
( OldFileInfoByHandle[ i ].OldFileHandle != INVALID_HANDLE_VALUE )) {
|
|
|
|
CloseHandle( OldFileInfoByHandle[ i ].OldFileHandle );
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, OldFileInfoByHandle);
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileExW(
|
|
IN ULONG OldFileCount,
|
|
IN PPATCH_OLD_FILE_INFO_W OldFileInfoArray,
|
|
IN LPCWSTR NewFileName,
|
|
OUT LPCWSTR PatchFileName,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData, // optional
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
PPATCH_OLD_FILE_INFO_H OldFileInfoByHandle = NULL;
|
|
HANDLE PatchFileHandle;
|
|
HANDLE NewFileHandle;
|
|
BOOL Success;
|
|
ULONG i;
|
|
|
|
if ( OldFileCount == 0 ) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
OldFileInfoByHandle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, OldFileCount * sizeof(PATCH_OLD_FILE_INFO_H));
|
|
if (!OldFileInfoByHandle) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
Success = TRUE;
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
|
|
OldFileInfoByHandle[ i ].SizeOfThisStruct = sizeof( PATCH_OLD_FILE_INFO_H );
|
|
OldFileInfoByHandle[ i ].IgnoreRangeCount = OldFileInfoArray[ i ].IgnoreRangeCount;
|
|
OldFileInfoByHandle[ i ].IgnoreRangeArray = OldFileInfoArray[ i ].IgnoreRangeArray;
|
|
OldFileInfoByHandle[ i ].RetainRangeCount = OldFileInfoArray[ i ].RetainRangeCount;
|
|
OldFileInfoByHandle[ i ].RetainRangeArray = OldFileInfoArray[ i ].RetainRangeArray;
|
|
|
|
OldFileInfoByHandle[ i ].OldFileHandle = CreateFileW(
|
|
OldFileInfoArray[ i ].OldFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( OldFileInfoByHandle[ i ].OldFileHandle == INVALID_HANDLE_VALUE ) {
|
|
Success = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
Success = FALSE;
|
|
|
|
NewFileHandle = CreateFileW(
|
|
NewFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( NewFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
PatchFileHandle = CreateFileW(
|
|
PatchFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = CreatePatchFileByHandlesEx(
|
|
OldFileCount,
|
|
OldFileInfoByHandle,
|
|
NewFileHandle,
|
|
PatchFileHandle,
|
|
OptionFlags,
|
|
OptionData,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileW( PatchFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( NewFileHandle );
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
if (( OldFileInfoByHandle[ i ].OldFileHandle != NULL ) &&
|
|
( OldFileInfoByHandle[ i ].OldFileHandle != INVALID_HANDLE_VALUE )) {
|
|
|
|
CloseHandle( OldFileInfoByHandle[ i ].OldFileHandle );
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, OldFileInfoByHandle);
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
CreatePatchFileByHandlesEx(
|
|
IN ULONG OldFileCount,
|
|
IN PPATCH_OLD_FILE_INFO_H OldFileInfoArray,
|
|
IN HANDLE NewFileHandle,
|
|
OUT HANDLE PatchFileHandle,
|
|
IN ULONG OptionFlags,
|
|
IN PPATCH_OPTION_DATA OptionData, // optional
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
PATCH_HEADER_INFO HeaderInfo;
|
|
UP_IMAGE_NT_HEADERS32 NtHeader;
|
|
UP_IMAGE_NT_HEADERS32 OldFileNtHeader;
|
|
PPATCH_DATA PatchArray;
|
|
PFILETIME PatchFileTime;
|
|
FILETIME NewFileTime;
|
|
PUCHAR NewFileMapped;
|
|
ULONG NewFileSize;
|
|
ULONG NewFileCrc;
|
|
ULONG NewFileCoffBase;
|
|
ULONG NewFileCoffTime;
|
|
ULONG NewFileResTime;
|
|
ULONG NewFileCompressedSize;
|
|
PUCHAR OldFileMapped;
|
|
ULONG OldFileSize;
|
|
ULONG OldFileCrc;
|
|
PUCHAR PatchFileMapped;
|
|
PUCHAR PatchBuffer;
|
|
ULONG PatchBufferSize;
|
|
PUCHAR PatchAltBuffer;
|
|
ULONG PatchAltSize;
|
|
ULONG PatchDataSize;
|
|
ULONG PatchFileCrc;
|
|
ULONG HeaderSize;
|
|
ULONG HeaderOldFileCount;
|
|
ULONG ProgressPosition;
|
|
ULONG ProgressMaximum;
|
|
ULONG ErrorCode;
|
|
BOOL TryLzxBoth;
|
|
BOOL Success;
|
|
BOOL Transform;
|
|
HANDLE SubAllocatorHandle;
|
|
ULONG EstimatedLzxMemory;
|
|
ULONG ExtendedOptionFlags;
|
|
ULONG AltExtendedOptionFlags;
|
|
ULONG OldFileOriginalChecksum;
|
|
ULONG OldFileOriginalTimeDate;
|
|
ULONG i, j;
|
|
PUCHAR p;
|
|
ULONG LargestOldFileSize = 0;
|
|
|
|
if (( OldFileCount == 0 ) || ( OldFileCount > 127 )) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( OptionFlags & PATCH_OPTION_SIGNATURE_MD5 ) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
HeaderInfo.OldFileInfoArray = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, OldFileCount * sizeof( HEADER_OLD_FILE_INFO ));
|
|
if (!HeaderInfo.OldFileInfoArray) {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
PatchArray = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, OldFileCount * sizeof( PATCH_DATA ));
|
|
if (!PatchArray) {
|
|
HeapFree(GetProcessHeap(), 0, HeaderInfo.OldFileInfoArray);
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (( OptionFlags & 0x0000FFFF ) == PATCH_OPTION_USE_BEST ) {
|
|
OptionFlags |= PATCH_OPTION_USE_LZX_BEST;
|
|
}
|
|
|
|
for ( i = 1; i < OldFileCount; i++ ) {
|
|
if ( OldFileInfoArray[ i ].RetainRangeCount != OldFileInfoArray[ 0 ].RetainRangeCount ) {
|
|
HeapFree(GetProcessHeap(), 0, PatchArray);
|
|
HeapFree(GetProcessHeap(), 0, HeaderInfo.OldFileInfoArray);
|
|
SetLastError( ERROR_PATCH_RETAIN_RANGES_DIFFER );
|
|
return FALSE;
|
|
}
|
|
|
|
for ( j = 0; j < OldFileInfoArray[ 0 ].RetainRangeCount; j++ ) {
|
|
if (( OldFileInfoArray[ i ].RetainRangeArray[ j ].OffsetInNewFile !=
|
|
OldFileInfoArray[ 0 ].RetainRangeArray[ j ].OffsetInNewFile ) ||
|
|
( OldFileInfoArray[ i ].RetainRangeArray[ j ].LengthInBytes !=
|
|
OldFileInfoArray[ 0 ].RetainRangeArray[ j ].LengthInBytes )) {
|
|
|
|
HeapFree(GetProcessHeap(), 0, PatchArray);
|
|
HeapFree(GetProcessHeap(), 0, HeaderInfo.OldFileInfoArray);
|
|
SetLastError( ERROR_PATCH_RETAIN_RANGES_DIFFER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
ExtendedOptionFlags = 0;
|
|
|
|
if (( OptionData ) && ( OptionData->SizeOfThisStruct >= sizeof( PATCH_OPTION_DATA ))) {
|
|
ExtendedOptionFlags = OptionData->ExtendedOptionFlags;
|
|
}
|
|
|
|
Success = MyMapViewOfFileByHandle(
|
|
NewFileHandle,
|
|
&NewFileSize,
|
|
&NewFileMapped
|
|
);
|
|
|
|
if ( ! Success ) {
|
|
|
|
if ( GetLastError() == ERROR_SUCCESS ) {
|
|
|
|
SetLastError( ERROR_EXTENDED_ERROR );
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, PatchArray);
|
|
HeapFree(GetProcessHeap(), 0, HeaderInfo.OldFileInfoArray);
|
|
return FALSE;
|
|
}
|
|
|
|
GetFileTime( NewFileHandle, NULL, NULL, &NewFileTime );
|
|
PatchFileTime = &NewFileTime;
|
|
|
|
NewFileCoffBase = 0;
|
|
NewFileCoffTime = 0;
|
|
NewFileResTime = 0;
|
|
HeaderOldFileCount = 0;
|
|
HeaderSize = 0;
|
|
NewFileCrc = 0; // prevent compiler warning
|
|
|
|
ProgressPosition = 0;
|
|
ProgressMaximum = 0; // prevent compiler warning
|
|
|
|
__try {
|
|
|
|
NtHeader = GetNtHeader( NewFileMapped, NewFileSize );
|
|
|
|
if ( ! ( OptionFlags & PATCH_OPTION_NO_REBASE )) {
|
|
if ( NtHeader ) {
|
|
NewFileCoffTime = NtHeader->FileHeader.TimeDateStamp;
|
|
NewFileCoffBase = NtHeader->OptionalHeader.ImageBase;
|
|
}
|
|
else {
|
|
OptionFlags |= PATCH_OPTION_NO_REBASE;
|
|
}
|
|
}
|
|
|
|
if (( NtHeader ) && ( NtHeader->OptionalHeader.CheckSum == 0 )) {
|
|
OptionFlags |= PATCH_OPTION_NO_CHECKSUM;
|
|
}
|
|
|
|
if ( ! ( OptionFlags & PATCH_OPTION_NO_RESTIMEFIX )) {
|
|
|
|
if ( NtHeader ) {
|
|
|
|
UP_IMAGE_RESOURCE_DIRECTORY ResDir;
|
|
|
|
ResDir = ImageDirectoryMappedAddress(
|
|
NtHeader,
|
|
IMAGE_DIRECTORY_ENTRY_RESOURCE,
|
|
NULL,
|
|
NewFileMapped,
|
|
NewFileSize
|
|
);
|
|
|
|
if ( ResDir ) {
|
|
NewFileResTime = ResDir->TimeDateStamp;
|
|
}
|
|
}
|
|
|
|
if ( NewFileResTime == 0 ) {
|
|
OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX;
|
|
}
|
|
}
|
|
|
|
TryLzxBoth = FALSE;
|
|
|
|
if (( OptionFlags & PATCH_OPTION_USE_LZX_BEST ) == PATCH_OPTION_USE_LZX_BEST ) {
|
|
|
|
OptionFlags &= ~PATCH_OPTION_USE_LZX_B; // No E8 translation on first try.
|
|
|
|
if ((( ! NtHeader ) && ( *(UNALIGNED USHORT *)NewFileMapped == 0x5A4D )) || // MZ, not PE
|
|
(( NtHeader ) && ( NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 ))) { // PE, i386
|
|
|
|
TryLzxBoth = TRUE; // Will force E8 translation on second try.
|
|
}
|
|
}
|
|
|
|
else if (( OptionFlags & PATCH_OPTION_USE_LZX_BEST ) == PATCH_OPTION_USE_LZX_B ) {
|
|
|
|
//
|
|
// Caller is requesting forced E8 translation, so disable E8
|
|
// transformation.
|
|
//
|
|
|
|
if (( NtHeader ) && ( NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 )) { // PE, i386
|
|
ExtendedOptionFlags |= PATCH_TRANSFORM_NO_RELCALLS;
|
|
}
|
|
}
|
|
|
|
ProgressMaximum = NewFileSize * OldFileCount;
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
OldFileSize = GetFileSize( OldFileInfoArray[ i ].OldFileHandle, NULL );
|
|
|
|
if ( LargestOldFileSize < OldFileSize ) {
|
|
LargestOldFileSize = OldFileSize;
|
|
}
|
|
|
|
ProgressMaximum += LzxInsertSize( OldFileSize, OptionFlags );
|
|
}
|
|
|
|
if ( TryLzxBoth ) {
|
|
ProgressMaximum *= 2;
|
|
}
|
|
|
|
if ( OptionFlags & PATCH_OPTION_FAIL_IF_BIGGER ) {
|
|
ProgressMaximum += NewFileSize;
|
|
}
|
|
|
|
Success = ProgressCallbackWrapper(
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
0,
|
|
ProgressMaximum
|
|
);
|
|
|
|
if ( ! Success ) {
|
|
__leave;
|
|
}
|
|
|
|
for ( j = 0; j < OldFileInfoArray[ 0 ].RetainRangeCount; j++ ) {
|
|
ZeroMemory(
|
|
OldFileInfoArray[ 0 ].RetainRangeArray[ j ].OffsetInNewFile + NewFileMapped,
|
|
OldFileInfoArray[ 0 ].RetainRangeArray[ j ].LengthInBytes
|
|
);
|
|
}
|
|
|
|
NewFileCrc = Crc32( 0xFFFFFFFF, NewFileMapped, NewFileSize ) ^ 0xFFFFFFFF;
|
|
|
|
PatchBufferSize = ROUNDUP2( NewFileSize + ( NewFileSize / 256 ), 0x10000 );
|
|
|
|
Success = FALSE;
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
|
|
Success = MyMapViewOfFileByHandle(
|
|
OldFileInfoArray[ i ].OldFileHandle,
|
|
&OldFileSize,
|
|
&OldFileMapped
|
|
);
|
|
|
|
if ( ! Success ) {
|
|
break;
|
|
}
|
|
|
|
OldFileOriginalChecksum = 0;
|
|
OldFileOriginalTimeDate = 0;
|
|
OldFileNtHeader = NULL;
|
|
|
|
__try {
|
|
|
|
OldFileNtHeader = GetNtHeader( OldFileMapped, OldFileSize );
|
|
|
|
if ( OldFileNtHeader ) {
|
|
|
|
OldFileOriginalChecksum = OldFileNtHeader->OptionalHeader.CheckSum;
|
|
OldFileOriginalTimeDate = OldFileNtHeader->FileHeader.TimeDateStamp;
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
}
|
|
|
|
Success = NormalizeOldFileImageForPatching(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
NewFileCoffBase,
|
|
NewFileCoffTime,
|
|
OldFileInfoArray[ i ].IgnoreRangeCount,
|
|
OldFileInfoArray[ i ].IgnoreRangeArray,
|
|
OldFileInfoArray[ i ].RetainRangeCount,
|
|
OldFileInfoArray[ i ].RetainRangeArray
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
Success = SafeCompleteCrc32( OldFileMapped, OldFileSize, &OldFileCrc );
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// First determine if this old file is the same as any already
|
|
// processed old files.
|
|
//
|
|
|
|
Success = FALSE;
|
|
|
|
for ( j = 0; j < HeaderOldFileCount; j++ ) {
|
|
|
|
if (( HeaderInfo.OldFileInfoArray[ j ].OldFileCrc == OldFileCrc ) &&
|
|
( HeaderInfo.OldFileInfoArray[ j ].OldFileSize == OldFileSize )) {
|
|
|
|
//
|
|
// We have to remap the other old file here to make the
|
|
// comparison.
|
|
//
|
|
|
|
PUCHAR CompareFileMapped;
|
|
ULONG CompareFileSize;
|
|
|
|
Success = MyMapViewOfFileByHandle(
|
|
HeaderInfo.OldFileInfoArray[ j ].OldFileHandle,
|
|
&CompareFileSize,
|
|
&CompareFileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
ASSERT( CompareFileSize == HeaderInfo.OldFileInfoArray[ j ].OldFileSize );
|
|
|
|
NormalizeOldFileImageForPatching(
|
|
CompareFileMapped,
|
|
CompareFileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
NewFileCoffBase,
|
|
NewFileCoffTime,
|
|
HeaderInfo.OldFileInfoArray[ j ].IgnoreRangeCount,
|
|
HeaderInfo.OldFileInfoArray[ j ].IgnoreRangeArray,
|
|
HeaderInfo.OldFileInfoArray[ j ].RetainRangeCount,
|
|
HeaderInfo.OldFileInfoArray[ j ].RetainRangeArray
|
|
);
|
|
|
|
__try {
|
|
Success = ( memcmp( CompareFileMapped, OldFileMapped, OldFileSize ) == 0 );
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
|
|
UnmapViewOfFile( CompareFileMapped );
|
|
|
|
if ( Success ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! Success ) {
|
|
|
|
//
|
|
// Now see if old file is same as new file.
|
|
//
|
|
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryAlloc = 0;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount = 0;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryArray = NULL;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftUsageArray = NULL;
|
|
|
|
PatchBuffer = NULL;
|
|
PatchDataSize = 0;
|
|
|
|
if (( NewFileCrc == OldFileCrc ) && ( NewFileSize == OldFileSize )) {
|
|
|
|
__try {
|
|
Success = ( memcmp( NewFileMapped, OldFileMapped, NewFileSize ) == 0 );
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( ! Success ) {
|
|
|
|
//
|
|
// It's a unique file, so create the patch for it.
|
|
//
|
|
// First we need to apply the transforms.
|
|
//
|
|
|
|
Transform = TRUE;
|
|
|
|
//
|
|
// NOTE: This test for NtHeader is a perf tweak
|
|
// for non-coff files. If we ever have any
|
|
// transformations for non-coff files, this
|
|
// test should be removed.
|
|
//
|
|
|
|
if (( NtHeader ) && ( OldFileNtHeader )) {
|
|
|
|
//
|
|
// See if rift table already provided by
|
|
// caller so we don't need to generate it.
|
|
//
|
|
|
|
if (( OptionData ) &&
|
|
( OptionData->SizeOfThisStruct >= sizeof( PATCH_OPTION_DATA )) &&
|
|
( OptionData->SymbolOptionFlags & PATCH_SYMBOL_EXTERNAL_RIFT ) &&
|
|
( OptionData->OldFileSymbolPathArray ) &&
|
|
( OptionData->OldFileSymbolPathArray[ i ] )) {
|
|
|
|
//
|
|
// This hidden private flag that tells us the rift information
|
|
// is already specified for us. The LPCSTR pointer at
|
|
// OptionData->OldFileSymbolPathArray[ i ] is really a
|
|
// PRIFT_TABLE pointer. Note that no validation of external
|
|
// rift data is performed (must be in ascending order with
|
|
// no OldRva duplicates).
|
|
//
|
|
// We need to be careful to treat this external rift table
|
|
// differently in that we don't want to free the arrays
|
|
// like we do for our internally allocated rift tables.
|
|
// So, mark the RiftEntryAlloc field as zero to indicate
|
|
// that the rift arrays were not internally allocated.
|
|
//
|
|
|
|
PRIFT_TABLE ExternalRiftTable = (PVOID) OptionData->OldFileSymbolPathArray[ i ];
|
|
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryAlloc = 0;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount = ExternalRiftTable->RiftEntryCount;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryArray = ExternalRiftTable->RiftEntryArray;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftUsageArray = ExternalRiftTable->RiftUsageArray;
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
// Need to allocate rift arrays and generate rift data.
|
|
// This (NewSize+OldSize)/sizeof(RIFT) allocation will
|
|
// provide enough space for one rift entry for every
|
|
// four bytes in the files.
|
|
//
|
|
|
|
ULONG AllocCount = ( NewFileSize + OldFileSize ) / sizeof( RIFT_ENTRY );
|
|
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount = 0;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryAlloc = AllocCount;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryArray = MyVirtualAlloc( AllocCount * sizeof( RIFT_ENTRY ));
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftUsageArray = MyVirtualAlloc( AllocCount * sizeof( UCHAR ));
|
|
|
|
if (( HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryArray == NULL ) ||
|
|
( HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftUsageArray == NULL )) {
|
|
|
|
Transform = FALSE;
|
|
}
|
|
|
|
else {
|
|
|
|
Transform = GenerateRiftTable(
|
|
OldFileInfoArray[ i ].OldFileHandle,
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
OldFileOriginalChecksum,
|
|
OldFileOriginalTimeDate,
|
|
NewFileHandle,
|
|
NewFileMapped,
|
|
NewFileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
i,
|
|
&HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable
|
|
);
|
|
|
|
ASSERT( HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount <= HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryAlloc );
|
|
|
|
#ifdef TESTCODE
|
|
printf( "\r%9d unique rift entries generated\n", HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ( Transform ) {
|
|
|
|
if ( HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount != 0 ) {
|
|
|
|
Transform = TransformOldFileImageForPatching(
|
|
ExtendedOptionFlags,
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileResTime,
|
|
&HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Transform ) {
|
|
|
|
PatchBuffer = MyVirtualAlloc( PatchBufferSize );
|
|
|
|
if ( PatchBuffer != NULL ) {
|
|
|
|
EstimatedLzxMemory = EstimateLzxCompressionMemoryRequirement(
|
|
OldFileSize,
|
|
NewFileSize,
|
|
OptionFlags
|
|
);
|
|
|
|
SubAllocatorHandle = CreateSubAllocator(
|
|
EstimatedLzxMemory,
|
|
MINIMUM_VM_ALLOCATION
|
|
);
|
|
|
|
if ( SubAllocatorHandle != NULL ) {
|
|
|
|
__try {
|
|
ErrorCode = CreateRawLzxPatchDataFromBuffers(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileMapped,
|
|
NewFileSize,
|
|
PatchBufferSize,
|
|
PatchBuffer,
|
|
&PatchDataSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
SubAllocate,
|
|
SubAllocatorHandle,
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
ProgressPosition,
|
|
ProgressMaximum
|
|
);
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ErrorCode = GetExceptionCode();
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocatorHandle );
|
|
|
|
if ( ErrorCode == NO_ERROR ) {
|
|
|
|
Success = TRUE;
|
|
|
|
if ( TryLzxBoth ) {
|
|
|
|
AltExtendedOptionFlags = ExtendedOptionFlags;
|
|
|
|
if (( NtHeader ) && ( ! ( AltExtendedOptionFlags | PATCH_TRANSFORM_NO_RELCALLS ))) {
|
|
|
|
//
|
|
// Need to map, normalize, and transform
|
|
// old file again without E8 transform.
|
|
//
|
|
|
|
AltExtendedOptionFlags |= PATCH_TRANSFORM_NO_RELCALLS;
|
|
|
|
UnmapViewOfFile( OldFileMapped );
|
|
OldFileMapped = NULL;
|
|
|
|
Success = MyMapViewOfFileByHandle(
|
|
OldFileInfoArray[ i ].OldFileHandle,
|
|
&OldFileSize,
|
|
&OldFileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
Success = NormalizeOldFileImageForPatching(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
NewFileCoffBase,
|
|
NewFileCoffTime,
|
|
OldFileInfoArray[ i ].IgnoreRangeCount,
|
|
OldFileInfoArray[ i ].IgnoreRangeArray,
|
|
OldFileInfoArray[ i ].RetainRangeCount,
|
|
OldFileInfoArray[ i ].RetainRangeArray
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
if ( HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount != 0 ) {
|
|
|
|
Success = TransformOldFileImageForPatching(
|
|
AltExtendedOptionFlags,
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileResTime,
|
|
&HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
PatchAltBuffer = MyVirtualAlloc( PatchBufferSize );
|
|
|
|
if ( PatchAltBuffer != NULL ) {
|
|
|
|
SubAllocatorHandle = CreateSubAllocator(
|
|
EstimatedLzxMemory,
|
|
MINIMUM_VM_ALLOCATION
|
|
);
|
|
|
|
if ( SubAllocatorHandle != NULL ) {
|
|
|
|
PatchAltSize = 0; // prevent compiler warning
|
|
|
|
__try {
|
|
ErrorCode = CreateRawLzxPatchDataFromBuffers(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileMapped,
|
|
NewFileSize,
|
|
PatchBufferSize,
|
|
PatchAltBuffer,
|
|
&PatchAltSize,
|
|
OptionFlags | PATCH_OPTION_USE_LZX_B,
|
|
OptionData,
|
|
SubAllocate,
|
|
SubAllocatorHandle,
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
ProgressPosition + NewFileSize + LzxInsertSize( OldFileSize, OptionFlags ),
|
|
ProgressMaximum
|
|
);
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ErrorCode = GetExceptionCode();
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocatorHandle );
|
|
|
|
if (( ErrorCode == NO_ERROR ) && ( PatchAltSize <= PatchDataSize )) {
|
|
MyVirtualFree( PatchBuffer );
|
|
PatchBuffer = PatchAltBuffer;
|
|
PatchDataSize = PatchAltSize;
|
|
ExtendedOptionFlags = AltExtendedOptionFlags;
|
|
}
|
|
else {
|
|
MyVirtualFree( PatchAltBuffer );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
SetLastError( ErrorCode );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
PatchArray[ HeaderOldFileCount ].PatchData = PatchBuffer;
|
|
PatchArray[ HeaderOldFileCount ].PatchSize = PatchDataSize;
|
|
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].OldFileHandle = OldFileInfoArray[ i ].OldFileHandle;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].OldFileSize = OldFileSize;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].OldFileCrc = OldFileCrc;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].PatchDataSize = PatchDataSize;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].IgnoreRangeCount = OldFileInfoArray[ i ].IgnoreRangeCount;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].IgnoreRangeArray = OldFileInfoArray[ i ].IgnoreRangeArray;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RetainRangeCount = OldFileInfoArray[ i ].RetainRangeCount;
|
|
HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RetainRangeArray = OldFileInfoArray[ i ].RetainRangeArray;
|
|
|
|
//
|
|
// We overestimate (worst case) the possible
|
|
// header size here. Note that typical rift
|
|
// encoding size is around 5 bytes per entry,
|
|
// but we expect that to decrease when we switch
|
|
// to Huffman encoding for the rift table.
|
|
//
|
|
|
|
HeaderSize += 32;
|
|
HeaderSize += HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].IgnoreRangeCount * sizeof( PATCH_IGNORE_RANGE );
|
|
HeaderSize += HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RetainRangeCount * sizeof( PATCH_RETAIN_RANGE );
|
|
HeaderSize += HeaderInfo.OldFileInfoArray[ HeaderOldFileCount ].RiftTable.RiftEntryCount * sizeof( RIFT_ENTRY );
|
|
|
|
++HeaderOldFileCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( OldFileMapped != NULL ) {
|
|
UnmapViewOfFile( OldFileMapped );
|
|
OldFileMapped = NULL;
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
ProgressPosition += ( LzxInsertSize( OldFileSize, OptionFlags ) + NewFileSize ) * ( TryLzxBoth ? 2 : 1 );
|
|
|
|
Success = ProgressCallbackWrapper(
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
ProgressPosition,
|
|
ProgressMaximum
|
|
);
|
|
}
|
|
|
|
if ( ! Success ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
if (( OptionFlags & PATCH_OPTION_FAIL_IF_SAME_FILE ) &&
|
|
( HeaderOldFileCount == 1 ) &&
|
|
( PatchArray[ 0 ].PatchSize == 0 )) {
|
|
|
|
SetLastError( ERROR_PATCH_SAME_FILE );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
PatchBuffer = NULL;
|
|
PatchDataSize = 0;
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// Create header
|
|
//
|
|
|
|
Success = FALSE;
|
|
|
|
HeaderSize = ROUNDUP2( HeaderSize, 0x10000 );
|
|
|
|
PatchBuffer = MyVirtualAlloc( HeaderSize );
|
|
|
|
if ( PatchBuffer != NULL ) {
|
|
|
|
Success = TRUE;
|
|
|
|
//
|
|
// Compute size of PatchData without the header.
|
|
//
|
|
|
|
PatchDataSize = 0;
|
|
|
|
for ( i = 0; i < HeaderOldFileCount; i++ ) {
|
|
PatchDataSize += PatchArray[ i ].PatchSize;
|
|
}
|
|
|
|
//
|
|
// Don't need to encode NewFileResTime if the patch is simply
|
|
// a header with no patch data (new file is same as old file).
|
|
// We do still need the NewFileCoffTime and NewFileCoffBase
|
|
// though because we still need to normalize the old file.
|
|
//
|
|
|
|
if ( PatchDataSize == 0 ) {
|
|
OptionFlags |= PATCH_OPTION_NO_RESTIMEFIX;
|
|
NewFileResTime = 0;
|
|
}
|
|
|
|
if ( ExtendedOptionFlags ) {
|
|
OptionFlags |= PATCH_OPTION_EXTENDED_OPTIONS;
|
|
}
|
|
else {
|
|
OptionFlags &= ~PATCH_OPTION_EXTENDED_OPTIONS;
|
|
}
|
|
|
|
//
|
|
// Don't need to set PATCH_OPTION_LZX_LARGE unless an LZX window larger
|
|
// than 8Mb was used. This allows backwards compatibility by default for
|
|
// files smaller than 8Mb.
|
|
//
|
|
|
|
if ( OptionFlags & PATCH_OPTION_USE_LZX_LARGE ) {
|
|
|
|
if ( LzxWindowSize( LargestOldFileSize, NewFileSize, OptionFlags ) <= LZX_MAXWINDOW_8 ) {
|
|
|
|
OptionFlags &= ~PATCH_OPTION_USE_LZX_LARGE;
|
|
}
|
|
}
|
|
|
|
|
|
HeaderInfo.Signature = PATCH_SIGNATURE;
|
|
HeaderInfo.OptionFlags = OptionFlags;
|
|
HeaderInfo.ExtendedOptionFlags = ExtendedOptionFlags;
|
|
HeaderInfo.OptionData = OptionData;
|
|
HeaderInfo.NewFileCoffBase = NewFileCoffBase;
|
|
HeaderInfo.NewFileCoffTime = NewFileCoffTime;
|
|
HeaderInfo.NewFileResTime = NewFileResTime;
|
|
HeaderInfo.NewFileSize = NewFileSize;
|
|
HeaderInfo.NewFileCrc = NewFileCrc;
|
|
HeaderInfo.OldFileCount = HeaderOldFileCount;
|
|
HeaderInfo.NewFileTime = 0;
|
|
|
|
if ( ! ( OptionFlags & PATCH_OPTION_NO_TIMESTAMP )) {
|
|
|
|
HeaderInfo.NewFileTime = FileTimeToUlongTime( &NewFileTime );
|
|
PatchFileTime = NULL;
|
|
}
|
|
|
|
HeaderSize = EncodePatchHeader( &HeaderInfo, PatchBuffer );
|
|
|
|
PatchDataSize += HeaderSize + sizeof( ULONG );
|
|
|
|
//
|
|
// Now we know the size of the patch file, so if we want to
|
|
// make sure it's not bigger than just compressing the new
|
|
// file, we need to compress the new file to see (the output
|
|
// of the compression is discarded -- we just want to know
|
|
// how big it would be. Obviously if the patch file is bigger
|
|
// than the raw new file, no need to compress the new file to
|
|
// see if that is smaller!
|
|
//
|
|
|
|
if ( OptionFlags & PATCH_OPTION_FAIL_IF_BIGGER ) {
|
|
|
|
if ( PatchDataSize > NewFileSize ) {
|
|
SetLastError( ERROR_PATCH_BIGGER_THAN_COMPRESSED );
|
|
Success = FALSE;
|
|
}
|
|
|
|
else {
|
|
|
|
EstimatedLzxMemory = EstimateLzxCompressionMemoryRequirement(
|
|
0,
|
|
NewFileSize,
|
|
0 // CAB has only 2Mb window size
|
|
);
|
|
|
|
SubAllocatorHandle = CreateSubAllocator(
|
|
EstimatedLzxMemory,
|
|
MINIMUM_VM_ALLOCATION
|
|
);
|
|
|
|
if ( SubAllocatorHandle != NULL ) {
|
|
|
|
NewFileCompressedSize = 0; // prevent compiler warning
|
|
|
|
__try {
|
|
ErrorCode = RawLzxCompressBuffer(
|
|
NewFileMapped,
|
|
NewFileSize,
|
|
0,
|
|
NULL,
|
|
&NewFileCompressedSize,
|
|
SubAllocate,
|
|
SubAllocatorHandle,
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
ProgressPosition,
|
|
ProgressMaximum
|
|
);
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ErrorCode = GetExceptionCode();
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocatorHandle );
|
|
|
|
if ( ErrorCode == NO_ERROR ) {
|
|
if ( PatchDataSize > NewFileCompressedSize ) {
|
|
SetLastError( ERROR_PATCH_BIGGER_THAN_COMPRESSED );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
ProgressPosition += NewFileSize;
|
|
|
|
Success = ProgressCallbackWrapper(
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
ProgressPosition,
|
|
ProgressMaximum
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UnmapViewOfFile( NewFileMapped );
|
|
|
|
if ( Success ) {
|
|
|
|
Success = MyCreateMappedFileByHandle(
|
|
PatchFileHandle,
|
|
PatchDataSize,
|
|
&PatchFileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
__try {
|
|
|
|
p = PatchFileMapped;
|
|
CopyMemory( p, PatchBuffer, HeaderSize );
|
|
p += HeaderSize;
|
|
|
|
for ( i = 0; i < HeaderOldFileCount; i++ ) {
|
|
if ( PatchArray[ i ].PatchSize != 0 ) {
|
|
CopyMemory( p, PatchArray[ i ].PatchData, PatchArray[ i ].PatchSize );
|
|
p += PatchArray[ i ].PatchSize;
|
|
}
|
|
}
|
|
|
|
PatchFileCrc = Crc32( 0xFFFFFFFF, PatchFileMapped, PatchDataSize - sizeof( ULONG ));
|
|
|
|
*(UNALIGNED ULONG *)( PatchFileMapped + PatchDataSize - sizeof( ULONG )) = PatchFileCrc;
|
|
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
PatchDataSize = 0;
|
|
Success = FALSE;
|
|
}
|
|
|
|
MyUnmapCreatedMappedFile(
|
|
PatchFileHandle,
|
|
PatchFileMapped,
|
|
PatchDataSize,
|
|
PatchFileTime
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
|
|
if ( PatchBuffer ) {
|
|
MyVirtualFree( PatchBuffer );
|
|
}
|
|
|
|
for ( i = 0; i < OldFileCount; i++ ) {
|
|
if ( PatchArray[ i ].PatchData ) {
|
|
MyVirtualFree( PatchArray[ i ].PatchData );
|
|
}
|
|
if ( HeaderInfo.OldFileInfoArray[ i ].RiftTable.RiftEntryAlloc ) {
|
|
if ( HeaderInfo.OldFileInfoArray[ i ].RiftTable.RiftEntryArray ) {
|
|
MyVirtualFree( HeaderInfo.OldFileInfoArray[ i ].RiftTable.RiftEntryArray );
|
|
}
|
|
if ( HeaderInfo.OldFileInfoArray[ i ].RiftTable.RiftUsageArray ) {
|
|
MyVirtualFree( HeaderInfo.OldFileInfoArray[ i ].RiftTable.RiftUsageArray );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Success ) {
|
|
ASSERT( ProgressPosition == ProgressMaximum );
|
|
}
|
|
|
|
if (( ! Success ) &&
|
|
( GetLastError() == ERROR_SUCCESS )) {
|
|
|
|
SetLastError( ERROR_EXTENDED_ERROR );
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, PatchArray);
|
|
HeapFree(GetProcessHeap(), 0, HeaderInfo.OldFileInfoArray);
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ExtractPatchHeaderToFileA(
|
|
IN LPCSTR PatchFileName,
|
|
OUT LPCSTR PatchHeaderFileName
|
|
)
|
|
{
|
|
HANDLE PatchFileHandle;
|
|
HANDLE HeaderFileHandle;
|
|
BOOL Success = FALSE;
|
|
|
|
PatchFileHandle = CreateFileA(
|
|
PatchFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_RANDOM_ACCESS,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
HeaderFileHandle = CreateFileA(
|
|
PatchHeaderFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
|
|
if ( HeaderFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = ExtractPatchHeaderToFileByHandles(
|
|
PatchFileHandle,
|
|
HeaderFileHandle
|
|
);
|
|
|
|
CloseHandle( HeaderFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileA( PatchHeaderFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ExtractPatchHeaderToFileW(
|
|
IN LPCWSTR PatchFileName,
|
|
OUT LPCWSTR PatchHeaderFileName
|
|
)
|
|
{
|
|
HANDLE PatchFileHandle;
|
|
HANDLE HeaderFileHandle;
|
|
BOOL Success = FALSE;
|
|
|
|
PatchFileHandle = CreateFileW(
|
|
PatchFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_RANDOM_ACCESS,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
HeaderFileHandle = CreateFileW(
|
|
PatchHeaderFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
|
|
if ( HeaderFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = ExtractPatchHeaderToFileByHandles(
|
|
PatchFileHandle,
|
|
HeaderFileHandle
|
|
);
|
|
|
|
CloseHandle( HeaderFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileW( PatchHeaderFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ExtractPatchHeaderToFileByHandles(
|
|
IN HANDLE PatchFileHandle,
|
|
OUT HANDLE PatchHeaderFileHandle
|
|
)
|
|
{
|
|
PPATCH_HEADER_INFO HeaderInfo;
|
|
HANDLE SubAllocator;
|
|
PUCHAR PatchFileMapped;
|
|
FILETIME PatchFileTime;
|
|
ULONG PatchFileSize;
|
|
ULONG PatchFileCrc;
|
|
ULONG PatchHeaderSize;
|
|
ULONG ActualSize;
|
|
ULONG i;
|
|
BOOL Success;
|
|
BOOL Mapped;
|
|
|
|
Success = FALSE;
|
|
|
|
Mapped = MyMapViewOfFileByHandle(
|
|
PatchFileHandle,
|
|
&PatchFileSize,
|
|
&PatchFileMapped
|
|
);
|
|
|
|
if ( Mapped ) {
|
|
|
|
GetFileTime( PatchFileHandle, NULL, NULL, &PatchFileTime );
|
|
|
|
PatchFileCrc = 0;
|
|
|
|
SafeCompleteCrc32( PatchFileMapped, PatchFileSize, &PatchFileCrc );
|
|
|
|
if ( PatchFileCrc == 0xFFFFFFFF ) {
|
|
|
|
SubAllocator = CreateSubAllocator( 0x10000, 0x10000 );
|
|
|
|
if ( SubAllocator ) {
|
|
|
|
Success = DecodePatchHeader(
|
|
PatchFileMapped,
|
|
PatchFileSize,
|
|
SubAllocator,
|
|
&PatchHeaderSize,
|
|
&HeaderInfo
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// Header extraction is provided so that a header without
|
|
// the bulk of the patch data can be used to determine if
|
|
// an old file is correct for this patch header (can be
|
|
// patched).
|
|
//
|
|
// Since the extracted header will not be used to actually
|
|
// apply, we don't need any of the header data that is
|
|
// used only for transformation (RiftTable and NewResTime).
|
|
// Since NewResTime is typically encoded as one byte (as
|
|
// delta from NewCoffTime), we won't bother throwing it
|
|
// away, but we will throw away the RiftTable.
|
|
//
|
|
// Zero out the rift entry counts, then re-create the
|
|
// patch header with the zeroed rift counts (create over
|
|
// the write-copy mapped patch file buffer, then write
|
|
// that buffer to disk).
|
|
//
|
|
|
|
for ( i = 0; i < HeaderInfo->OldFileCount; i++ ) {
|
|
HeaderInfo->OldFileInfoArray[ i ].RiftTable.RiftEntryCount = 0;
|
|
}
|
|
|
|
__try {
|
|
|
|
PatchHeaderSize = EncodePatchHeader( HeaderInfo, PatchFileMapped );
|
|
|
|
PatchFileCrc = Crc32( 0xFFFFFFFF, PatchFileMapped, PatchHeaderSize );
|
|
|
|
*(UNALIGNED ULONG *)( PatchFileMapped + PatchHeaderSize ) = PatchFileCrc;
|
|
|
|
Success = WriteFile(
|
|
PatchHeaderFileHandle,
|
|
PatchFileMapped,
|
|
PatchHeaderSize + sizeof( ULONG ),
|
|
&ActualSize,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Success = FALSE;
|
|
}
|
|
|
|
if ( Success ) {
|
|
SetFileTime( PatchHeaderFileHandle, NULL, NULL, &PatchFileTime );
|
|
}
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocator );
|
|
}
|
|
}
|
|
|
|
else {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
}
|
|
|
|
UnmapViewOfFile( PatchFileMapped );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
#endif // ! PATCH_APPLY_CODE_ONLY
|
|
|
|
|
|
//
|
|
// Following group of functions and exported apis are exclusively for
|
|
// applying patches. If we're only compiling the create code, ignore
|
|
// this group of functions.
|
|
//
|
|
|
|
#ifndef PATCH_CREATE_CODE_ONLY
|
|
|
|
PVOID
|
|
SaveRetainRanges(
|
|
IN PUCHAR MappedFile,
|
|
IN ULONG FileSize,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN BOOL SaveFromNewFile
|
|
)
|
|
{
|
|
PUCHAR Buffer, p;
|
|
ULONG Offset;
|
|
ULONG TotalSize = 0;
|
|
ULONG i;
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
TotalSize += RetainRangeArray[ i ].LengthInBytes;
|
|
}
|
|
|
|
Buffer = MyVirtualAlloc( TotalSize );
|
|
|
|
if ( Buffer ) {
|
|
|
|
__try {
|
|
|
|
p = Buffer;
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
|
|
Offset = SaveFromNewFile ?
|
|
RetainRangeArray[ i ].OffsetInNewFile :
|
|
RetainRangeArray[ i ].OffsetInOldFile;
|
|
|
|
if (( Offset + RetainRangeArray[ i ].LengthInBytes ) <= FileSize ) {
|
|
CopyMemory( p, MappedFile + Offset, RetainRangeArray[ i ].LengthInBytes );
|
|
}
|
|
|
|
p += RetainRangeArray[ i ].LengthInBytes;
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
MyVirtualFree( Buffer );
|
|
Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateNewFileFromOldFileMapped(
|
|
IN PUCHAR OldFileMapped,
|
|
IN ULONG OldFileSize,
|
|
OUT HANDLE NewFileHandle,
|
|
IN PFILETIME NewFileTime,
|
|
IN ULONG NewFileExpectedCrc,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN PUCHAR RetainBuffer,
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
PUCHAR NewFileMapped;
|
|
ULONG NewFileCrc;
|
|
BOOL Success;
|
|
ULONG i;
|
|
|
|
Success = MyCreateMappedFileByHandle(
|
|
NewFileHandle,
|
|
OldFileSize,
|
|
&NewFileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
__try {
|
|
|
|
CopyMemory( NewFileMapped, OldFileMapped, OldFileSize );
|
|
|
|
NewFileCrc = Crc32( 0xFFFFFFFF, NewFileMapped, OldFileSize ) ^ 0xFFFFFFFF;
|
|
|
|
if ( NewFileCrc == NewFileExpectedCrc ) {
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
if (( RetainRangeArray[ i ].OffsetInNewFile + RetainRangeArray[ i ].LengthInBytes ) <= OldFileSize ) {
|
|
CopyMemory(
|
|
RetainRangeArray[ i ].OffsetInNewFile + NewFileMapped,
|
|
RetainBuffer,
|
|
RetainRangeArray[ i ].LengthInBytes
|
|
);
|
|
}
|
|
RetainBuffer += RetainRangeArray[ i ].LengthInBytes;
|
|
}
|
|
|
|
Success = ProgressCallbackWrapper(
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
OldFileSize,
|
|
OldFileSize
|
|
);
|
|
}
|
|
|
|
else {
|
|
SetLastError( ERROR_PATCH_WRONG_FILE );
|
|
OldFileSize = 0;
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode());
|
|
OldFileSize = 0;
|
|
Success = FALSE;
|
|
}
|
|
|
|
MyUnmapCreatedMappedFile(
|
|
NewFileHandle,
|
|
NewFileMapped,
|
|
OldFileSize,
|
|
NewFileTime
|
|
);
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateNewFileFromPatchData(
|
|
IN PUCHAR OldFileMapped,
|
|
IN ULONG OldFileSize,
|
|
IN PUCHAR PatchData,
|
|
IN ULONG PatchDataSize,
|
|
OUT HANDLE NewFileHandle,
|
|
IN ULONG NewFileSize,
|
|
IN PFILETIME NewFileTime,
|
|
IN ULONG NewFileExpectedCrc,
|
|
IN ULONG RetainRangeCount,
|
|
IN PPATCH_RETAIN_RANGE RetainRangeArray,
|
|
IN PUCHAR RetainBuffer,
|
|
IN ULONG OptionFlags,
|
|
IN PVOID OptionData,
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
HANDLE SubAllocatorHandle;
|
|
ULONG EstimatedLzxMemory;
|
|
PUCHAR NewFileMapped;
|
|
ULONG NewFileCrc;
|
|
ULONG ErrorCode;
|
|
BOOL Success;
|
|
ULONG i;
|
|
|
|
UNREFERENCED_PARAMETER( OptionData );
|
|
|
|
Success = MyCreateMappedFileByHandle(
|
|
NewFileHandle,
|
|
NewFileSize,
|
|
&NewFileMapped
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
ErrorCode = NO_ERROR;
|
|
|
|
EstimatedLzxMemory = EstimateLzxDecompressionMemoryRequirement(
|
|
OldFileSize,
|
|
NewFileSize,
|
|
OptionFlags
|
|
);
|
|
|
|
SubAllocatorHandle = CreateSubAllocator(
|
|
EstimatedLzxMemory,
|
|
MINIMUM_VM_ALLOCATION
|
|
);
|
|
|
|
if ( SubAllocatorHandle != NULL ) {
|
|
|
|
__try {
|
|
|
|
ErrorCode = ApplyRawLzxPatchToBuffer(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
PatchData,
|
|
PatchDataSize,
|
|
NewFileMapped,
|
|
NewFileSize,
|
|
OptionFlags,
|
|
OptionData,
|
|
SubAllocate,
|
|
SubAllocatorHandle,
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
0,
|
|
NewFileSize
|
|
);
|
|
|
|
if ( ErrorCode == NO_ERROR ) {
|
|
|
|
NewFileCrc = Crc32( 0xFFFFFFFF, NewFileMapped, NewFileSize ) ^ 0xFFFFFFFF;
|
|
|
|
if ( NewFileCrc == NewFileExpectedCrc ) {
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
if (( RetainRangeArray[ i ].OffsetInNewFile + RetainRangeArray[ i ].LengthInBytes ) <= OldFileSize ) {
|
|
CopyMemory(
|
|
RetainRangeArray[ i ].OffsetInNewFile + NewFileMapped,
|
|
RetainBuffer,
|
|
RetainRangeArray[ i ].LengthInBytes
|
|
);
|
|
}
|
|
RetainBuffer += RetainRangeArray[ i ].LengthInBytes;
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
ErrorCode = ERROR_PATCH_WRONG_FILE;
|
|
|
|
}
|
|
}
|
|
|
|
#ifdef TESTCODE
|
|
|
|
if ( ErrorCode != NO_ERROR ) {
|
|
|
|
HANDLE hFile = CreateFile(
|
|
"Wrong.out",
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( hFile != INVALID_HANDLE_VALUE ) {
|
|
|
|
DWORD Actual;
|
|
|
|
WriteFile( hFile, NewFileMapped, NewFileSize, &Actual, NULL );
|
|
|
|
CloseHandle( hFile );
|
|
|
|
}
|
|
}
|
|
|
|
#endif // TESTCODE
|
|
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ErrorCode = GetExceptionCode();
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocatorHandle );
|
|
}
|
|
|
|
MyUnmapCreatedMappedFile(
|
|
NewFileHandle,
|
|
NewFileMapped,
|
|
( ErrorCode == NO_ERROR ) ? NewFileSize : 0,
|
|
NewFileTime
|
|
);
|
|
|
|
if ( ErrorCode == NO_ERROR ) {
|
|
Success = TRUE;
|
|
}
|
|
else {
|
|
SetLastError( ErrorCode );
|
|
Success = FALSE;
|
|
}
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileByHandlesEx(
|
|
IN HANDLE PatchFileHandle,
|
|
IN HANDLE OldFileHandle,
|
|
OUT HANDLE NewFileHandle,
|
|
IN ULONG ApplyOptionFlags,
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
PHEADER_OLD_FILE_INFO OldFileInfo;
|
|
PPATCH_HEADER_INFO HeaderInfo;
|
|
PPATCH_RETAIN_RANGE RetainRangeArray;
|
|
ULONG RetainRangeCount;
|
|
PUCHAR RetainBuffer;
|
|
HANDLE SubAllocator;
|
|
ULONG PatchHeaderSize;
|
|
FILETIME NewFileTime;
|
|
PUCHAR PatchFileMapped;
|
|
ULONG PatchFileSize;
|
|
ULONG PatchFileCrc;
|
|
PUCHAR PatchData;
|
|
PUCHAR OldFileMapped;
|
|
ULONG OldFileSize;
|
|
ULONG OldFileCrc;
|
|
BOOL Mapped;
|
|
BOOL Success;
|
|
BOOL Finished;
|
|
ULONG i;
|
|
|
|
Success = FALSE;
|
|
|
|
Mapped = MyMapViewOfFileByHandle(
|
|
PatchFileHandle,
|
|
&PatchFileSize,
|
|
&PatchFileMapped
|
|
);
|
|
|
|
if ( Mapped ) {
|
|
|
|
GetFileTime( PatchFileHandle, NULL, NULL, &NewFileTime );
|
|
|
|
PatchFileCrc = 0;
|
|
|
|
SafeCompleteCrc32( PatchFileMapped, PatchFileSize, &PatchFileCrc );
|
|
|
|
if ( PatchFileCrc == 0xFFFFFFFF ) {
|
|
|
|
SubAllocator = CreateSubAllocator( 0x10000, 0x10000 );
|
|
|
|
if ( SubAllocator ) {
|
|
|
|
Success = DecodePatchHeader(
|
|
PatchFileMapped,
|
|
PatchFileSize,
|
|
SubAllocator,
|
|
&PatchHeaderSize,
|
|
&HeaderInfo
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
//
|
|
// Patch is valid.
|
|
//
|
|
|
|
Success = ProgressCallbackWrapper(
|
|
ProgressCallback,
|
|
CallbackContext,
|
|
0,
|
|
HeaderInfo->NewFileSize
|
|
);
|
|
|
|
if ( Success ) {
|
|
|
|
Finished = FALSE;
|
|
Success = FALSE;
|
|
|
|
if (( ! ( HeaderInfo->OptionFlags & PATCH_OPTION_NO_TIMESTAMP )) &&
|
|
( HeaderInfo->NewFileTime != 0 )) {
|
|
|
|
UlongTimeToFileTime( HeaderInfo->NewFileTime, &NewFileTime );
|
|
}
|
|
|
|
OldFileSize = GetFileSize( OldFileHandle, NULL );
|
|
|
|
//
|
|
// First see if the old file is really the new file.
|
|
//
|
|
|
|
if ( OldFileSize == HeaderInfo->NewFileSize ) {
|
|
|
|
Mapped = MyMapViewOfFileByHandle(
|
|
OldFileHandle,
|
|
&OldFileSize,
|
|
&OldFileMapped
|
|
);
|
|
|
|
if ( ! Mapped ) {
|
|
Success = FALSE;
|
|
Finished = TRUE;
|
|
}
|
|
|
|
else {
|
|
|
|
RetainBuffer = NULL;
|
|
OldFileCrc = 0;
|
|
OldFileInfo = &HeaderInfo->OldFileInfoArray[ 0 ];
|
|
RetainRangeCount = OldFileInfo->RetainRangeCount;
|
|
RetainRangeArray = OldFileInfo->RetainRangeArray;
|
|
|
|
if (( RetainRangeCount != 0 ) &&
|
|
( ! ( ApplyOptionFlags & APPLY_OPTION_TEST_ONLY ))) {
|
|
|
|
RetainBuffer = SaveRetainRanges(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
RetainRangeCount,
|
|
RetainRangeArray,
|
|
TRUE
|
|
);
|
|
|
|
if ( RetainBuffer == NULL ) {
|
|
Finished = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( ! Finished ) {
|
|
|
|
__try {
|
|
|
|
//
|
|
// First see if they match exact, without
|
|
// normalizing.
|
|
//
|
|
|
|
for ( i = 0; i < RetainRangeCount; i++ ) {
|
|
if (( RetainRangeArray[ i ].OffsetInNewFile + RetainRangeArray[ i ].LengthInBytes ) <= OldFileSize ) {
|
|
ZeroMemory( OldFileMapped + RetainRangeArray[ i ].OffsetInNewFile, RetainRangeArray[ i ].LengthInBytes );
|
|
}
|
|
}
|
|
|
|
OldFileCrc = Crc32( 0xFFFFFFFF, OldFileMapped, OldFileSize ) ^ 0xFFFFFFFF;
|
|
|
|
if ( OldFileCrc != HeaderInfo->NewFileCrc ) {
|
|
|
|
//
|
|
// Don't match exact, so try with
|
|
// normalizing.
|
|
//
|
|
// NOTE: We're assuming here that the
|
|
// zeroed retain ranges don't overlap
|
|
// with the binding info that we're
|
|
// correcting.
|
|
//
|
|
|
|
NormalizeOldFileImageForPatching(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
HeaderInfo->OptionFlags,
|
|
HeaderInfo->OptionData,
|
|
HeaderInfo->NewFileCoffBase,
|
|
HeaderInfo->NewFileCoffTime,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
OldFileCrc = Crc32( 0xFFFFFFFF, OldFileMapped, OldFileSize ) ^ 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
__except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SetLastError( GetExceptionCode() );
|
|
Finished = TRUE;
|
|
}
|
|
|
|
if (( ! Finished ) &&
|
|
( OldFileCrc == HeaderInfo->NewFileCrc ) &&
|
|
( OldFileSize == HeaderInfo->NewFileSize )) {
|
|
|
|
Finished = TRUE;
|
|
|
|
if ( ApplyOptionFlags & APPLY_OPTION_FAIL_IF_EXACT ) {
|
|
SetLastError( ERROR_PATCH_NOT_NECESSARY );
|
|
Success = FALSE;
|
|
}
|
|
else if ( ApplyOptionFlags & APPLY_OPTION_TEST_ONLY ) {
|
|
Success = TRUE;
|
|
}
|
|
else {
|
|
Success = CreateNewFileFromOldFileMapped(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileHandle,
|
|
&NewFileTime,
|
|
HeaderInfo->NewFileCrc,
|
|
RetainRangeCount,
|
|
RetainRangeArray,
|
|
RetainBuffer,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( RetainBuffer != NULL ) {
|
|
MyVirtualFree( RetainBuffer );
|
|
}
|
|
}
|
|
|
|
UnmapViewOfFile( OldFileMapped );
|
|
}
|
|
}
|
|
|
|
if ( ! Finished ) {
|
|
|
|
//
|
|
// Now see if the old file matches one of the old
|
|
// files we have in our patch file. For each set
|
|
// of old file info in our patch file, we have to
|
|
// remap the old file to check it since each old
|
|
// file might have different ignore range parameters
|
|
// (we modify the buffer for the ignore ranges).
|
|
//
|
|
|
|
PatchData = PatchFileMapped + PatchHeaderSize;
|
|
Success = FALSE;
|
|
|
|
for ( i = 0; ( i < HeaderInfo->OldFileCount ) && ( ! Finished ) && ( ! Success ); i++ ) {
|
|
|
|
OldFileInfo = &HeaderInfo->OldFileInfoArray[ i ];
|
|
|
|
if ( OldFileInfo->OldFileSize == OldFileSize ) {
|
|
|
|
Mapped = MyMapViewOfFileByHandle(
|
|
OldFileHandle,
|
|
&OldFileSize,
|
|
&OldFileMapped
|
|
);
|
|
|
|
if ( ! Mapped ) {
|
|
Finished = TRUE;
|
|
}
|
|
|
|
else {
|
|
|
|
RetainBuffer = NULL;
|
|
|
|
if (( OldFileInfo->RetainRangeCount != 0 ) &&
|
|
( ! ( ApplyOptionFlags & APPLY_OPTION_TEST_ONLY ))) {
|
|
|
|
RetainBuffer = SaveRetainRanges(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
OldFileInfo->RetainRangeCount,
|
|
OldFileInfo->RetainRangeArray,
|
|
FALSE
|
|
);
|
|
|
|
if ( RetainBuffer == NULL ) {
|
|
Finished = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( ! Finished ) {
|
|
|
|
NormalizeOldFileImageForPatching(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
HeaderInfo->OptionFlags,
|
|
HeaderInfo->OptionData,
|
|
HeaderInfo->NewFileCoffBase,
|
|
HeaderInfo->NewFileCoffTime,
|
|
OldFileInfo->IgnoreRangeCount,
|
|
OldFileInfo->IgnoreRangeArray,
|
|
OldFileInfo->RetainRangeCount,
|
|
OldFileInfo->RetainRangeArray
|
|
);
|
|
|
|
OldFileCrc = 0;
|
|
|
|
if (( SafeCompleteCrc32( OldFileMapped, OldFileSize, &OldFileCrc )) &&
|
|
( OldFileCrc == OldFileInfo->OldFileCrc ) &&
|
|
( OldFileSize == OldFileInfo->OldFileSize )) {
|
|
|
|
//
|
|
// CRC's match
|
|
//
|
|
|
|
if ( OldFileInfo->PatchDataSize == 0 ) {
|
|
if ( ApplyOptionFlags & APPLY_OPTION_FAIL_IF_CLOSE ) {
|
|
SetLastError( ERROR_PATCH_NOT_NECESSARY );
|
|
Finished = TRUE;
|
|
}
|
|
else if ( ApplyOptionFlags & APPLY_OPTION_TEST_ONLY ) {
|
|
Success = TRUE;
|
|
}
|
|
else {
|
|
Success = CreateNewFileFromOldFileMapped(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
NewFileHandle,
|
|
&NewFileTime,
|
|
HeaderInfo->NewFileCrc,
|
|
OldFileInfo->RetainRangeCount,
|
|
OldFileInfo->RetainRangeArray,
|
|
RetainBuffer,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
if ( ! Success ) {
|
|
Finished = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
if ( ApplyOptionFlags & APPLY_OPTION_TEST_ONLY ) {
|
|
Success = TRUE;
|
|
}
|
|
else if (( PatchData + OldFileInfo->PatchDataSize ) > ( PatchFileMapped + PatchFileSize )) {
|
|
SetLastError( ERROR_PATCH_NOT_AVAILABLE );
|
|
Finished = TRUE;
|
|
}
|
|
else {
|
|
|
|
Success = TRUE;
|
|
|
|
if ( OldFileInfo->RiftTable.RiftEntryCount != 0 ) {
|
|
|
|
Success = TransformOldFileImageForPatching(
|
|
HeaderInfo->ExtendedOptionFlags,
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
HeaderInfo->NewFileResTime,
|
|
&OldFileInfo->RiftTable
|
|
);
|
|
}
|
|
|
|
if ( Success ) {
|
|
|
|
Success = CreateNewFileFromPatchData(
|
|
OldFileMapped,
|
|
OldFileSize,
|
|
PatchData,
|
|
OldFileInfo->PatchDataSize,
|
|
NewFileHandle,
|
|
HeaderInfo->NewFileSize,
|
|
&NewFileTime,
|
|
HeaderInfo->NewFileCrc,
|
|
OldFileInfo->RetainRangeCount,
|
|
OldFileInfo->RetainRangeArray,
|
|
RetainBuffer,
|
|
HeaderInfo->OptionFlags,
|
|
HeaderInfo->OptionData,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
}
|
|
|
|
if ( ! Success ) {
|
|
Finished = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( RetainBuffer != NULL ) {
|
|
MyVirtualFree( RetainBuffer );
|
|
}
|
|
}
|
|
|
|
UnmapViewOfFile( OldFileMapped );
|
|
}
|
|
}
|
|
|
|
PatchData += OldFileInfo->PatchDataSize;
|
|
}
|
|
|
|
if (( ! Finished ) && ( ! Success )) {
|
|
SetLastError( ERROR_PATCH_WRONG_FILE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DestroySubAllocator( SubAllocator );
|
|
}
|
|
}
|
|
|
|
else {
|
|
SetLastError( ERROR_PATCH_CORRUPT );
|
|
}
|
|
|
|
UnmapViewOfFile( PatchFileMapped );
|
|
}
|
|
|
|
if (( ! Success ) &&
|
|
( GetLastError() == ERROR_SUCCESS )) {
|
|
|
|
SetLastError( ERROR_EXTENDED_ERROR );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
TestApplyPatchToFileByHandles(
|
|
IN HANDLE PatchFileHandle, // requires GENERIC_READ access
|
|
IN HANDLE OldFileHandle, // requires GENERIC_READ access
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileByHandles(
|
|
PatchFileHandle,
|
|
OldFileHandle,
|
|
INVALID_HANDLE_VALUE,
|
|
ApplyOptionFlags | APPLY_OPTION_TEST_ONLY
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileByHandles(
|
|
IN HANDLE PatchFileHandle, // requires GENERIC_READ access
|
|
IN HANDLE OldFileHandle, // requires GENERIC_READ access
|
|
OUT HANDLE NewFileHandle, // requires GENERIC_READ | GENERIC_WRITE
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileByHandlesEx(
|
|
PatchFileHandle,
|
|
OldFileHandle,
|
|
NewFileHandle,
|
|
ApplyOptionFlags,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
TestApplyPatchToFileA(
|
|
IN LPCSTR PatchFileName,
|
|
IN LPCSTR OldFileName,
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileA(
|
|
PatchFileName,
|
|
OldFileName,
|
|
INVALID_HANDLE_VALUE,
|
|
ApplyOptionFlags | APPLY_OPTION_TEST_ONLY
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileA(
|
|
IN LPCSTR PatchFileName,
|
|
IN LPCSTR OldFileName,
|
|
OUT LPCSTR NewFileName,
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileExA(
|
|
PatchFileName,
|
|
OldFileName,
|
|
NewFileName,
|
|
ApplyOptionFlags,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileExA(
|
|
IN LPCSTR PatchFileName,
|
|
IN LPCSTR OldFileName,
|
|
OUT LPCSTR NewFileName,
|
|
IN ULONG ApplyOptionFlags,
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
HANDLE PatchFileHandle;
|
|
HANDLE OldFileHandle;
|
|
HANDLE NewFileHandle;
|
|
BOOL Success = FALSE;
|
|
|
|
PatchFileHandle = CreateFileA(
|
|
PatchFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
OldFileHandle = CreateFileA(
|
|
OldFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( OldFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
NewFileHandle = CreateFileA(
|
|
NewFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( NewFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = ApplyPatchToFileByHandlesEx(
|
|
PatchFileHandle,
|
|
OldFileHandle,
|
|
NewFileHandle,
|
|
ApplyOptionFlags,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
|
|
CloseHandle( NewFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileA( NewFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( OldFileHandle );
|
|
}
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
TestApplyPatchToFileW(
|
|
IN LPCWSTR PatchFileName,
|
|
IN LPCWSTR OldFileName,
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileW(
|
|
PatchFileName,
|
|
OldFileName,
|
|
INVALID_HANDLE_VALUE,
|
|
ApplyOptionFlags | APPLY_OPTION_TEST_ONLY
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileW(
|
|
IN LPCWSTR PatchFileName,
|
|
IN LPCWSTR OldFileName,
|
|
OUT LPCWSTR NewFileName,
|
|
IN ULONG ApplyOptionFlags
|
|
)
|
|
{
|
|
return ApplyPatchToFileExW(
|
|
PatchFileName,
|
|
OldFileName,
|
|
NewFileName,
|
|
ApplyOptionFlags,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PATCHAPI
|
|
ApplyPatchToFileExW(
|
|
IN LPCWSTR PatchFileName,
|
|
IN LPCWSTR OldFileName,
|
|
OUT LPCWSTR NewFileName,
|
|
IN ULONG ApplyOptionFlags,
|
|
IN PPATCH_PROGRESS_CALLBACK ProgressCallback,
|
|
IN PVOID CallbackContext
|
|
)
|
|
{
|
|
HANDLE PatchFileHandle;
|
|
HANDLE OldFileHandle;
|
|
HANDLE NewFileHandle;
|
|
BOOL Success = FALSE;
|
|
|
|
PatchFileHandle = CreateFileW(
|
|
PatchFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( PatchFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
OldFileHandle = CreateFileW(
|
|
OldFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if ( OldFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
NewFileHandle = CreateFileW(
|
|
NewFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if ( NewFileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
Success = ApplyPatchToFileByHandlesEx(
|
|
PatchFileHandle,
|
|
OldFileHandle,
|
|
NewFileHandle,
|
|
ApplyOptionFlags,
|
|
ProgressCallback,
|
|
CallbackContext
|
|
);
|
|
|
|
CloseHandle( NewFileHandle );
|
|
|
|
if ( ! Success ) {
|
|
DeleteFileW( NewFileName );
|
|
}
|
|
}
|
|
|
|
CloseHandle( OldFileHandle );
|
|
}
|
|
|
|
CloseHandle( PatchFileHandle );
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
#endif // ! PATCH_CREATE_CODE_ONLY
|
|
|
|
|
|
|
|
|