Mozilla/mozilla/xpinstall/src/nsInstallPatch.cpp
dveditz%netscape.com 70cb614dd9 Checkin for dbragg, Bug 16214 make "folder" params objects rather than path strings, r=dveditz.
Bug 17509 add BLOATY instrumentation to XPInstall. A couple of minor leak fixes


git-svn-id: svn://10.0.0.236/trunk@54103 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-21 06:03:23 +00:00

1254 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsFileSpec.h"
#include "prmem.h"
#include "nsInstall.h"
#include "nsInstallPatch.h"
#include "nsInstallResources.h"
#include "nsIDOMInstallVersion.h"
#include "zlib.h"
#include "gdiff.h"
#include "VerReg.h"
#include "ScheduledTasks.h"
#include "plstr.h"
#include "prlog.h"
#ifdef XP_MAC
#include "PatchableAppleSingle.h"
#endif
#define BUFSIZE 32768
#define OPSIZE 1
#define MAXCMDSIZE 12
#define SRCFILE 0
#define OUTFILE 1
#define getshort(s) (uint16)( ((uchar)*(s) << 8) + ((uchar)*((s)+1)) )
#define getlong(s) \
(uint32)( ((uchar)*(s) << 24) + ((uchar)*((s)+1) << 16 ) + \
((uchar)*((s)+2) << 8) + ((uchar)*((s)+3)) )
static int32 gdiff_parseHeader( pDIFFDATA dd );
static int32 gdiff_validateFile( pDIFFDATA dd, int file );
static int32 gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum );
static int32 gdiff_ApplyPatch( pDIFFDATA dd );
static int32 gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length );
static int32 gdiff_add( pDIFFDATA dd, uint32 count );
static int32 gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count );
static int32 gdiff_validateFile( pDIFFDATA dd, int file );
#ifdef WIN32
static PRBool su_unbind(char* oldsrc, char* newsrc);
#endif
MOZ_DECL_CTOR_COUNTER(nsInstallPatch);
nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
const nsString& inVRName,
const nsString& inVInfo,
const nsString& inJarLocation,
PRInt32 *error)
: nsInstallObject(inInstall)
{
MOZ_COUNT_CTOR(nsInstallPatch);
char tempTargetFile[MAXREGPATHLEN];
char* tempVersionString = inVRName.ToNewCString();
PRInt32 err = VR_GetPath(tempVersionString, MAXREGPATHLEN, tempTargetFile );
Recycle(tempVersionString);
if (err != REGERR_OK)
{
*error = nsInstall::NO_SUCH_COMPONENT;
return;
}
nsString folderSpec(tempTargetFile);
mPatchFile = nsnull;
mTargetFile = nsnull;
mPatchedFile = nsnull;
mRegistryName = new nsString(inVRName);
mJarLocation = new nsString(inJarLocation);
mTargetFile = new nsFileSpec(folderSpec);
mVersionInfo = new nsInstallVersion();
if (mRegistryName == nsnull ||
mJarLocation == nsnull ||
mTargetFile == nsnull ||
mVersionInfo == nsnull )
{
*error = nsInstall::OUT_OF_MEMORY;
return;
}
mVersionInfo->Init(inVInfo);
}
nsInstallPatch::nsInstallPatch( nsInstall* inInstall,
const nsString& inVRName,
const nsString& inVInfo,
const nsString& inJarLocation,
nsInstallFolder* folderSpec,
const nsString& inPartialPath,
PRInt32 *error)
: nsInstallObject(inInstall)
{
MOZ_COUNT_CTOR(nsInstallPatch);
if ((inInstall == nsnull) || (inVRName.Equals("")) || (inJarLocation.Equals("")))
{
*error = nsInstall::INVALID_ARGUMENTS;
return;
}
nsFileSpec* tmp = folderSpec->GetFileSpec();
if (!tmp)
{
*error = nsInstall::INVALID_ARGUMENTS;
return;
}
mPatchFile = nsnull;
mTargetFile = nsnull;
mPatchedFile = nsnull;
mRegistryName = new nsString(inVRName);
mJarLocation = new nsString(inJarLocation);
mVersionInfo = new nsInstallVersion();
mTargetFile = new nsFileSpec(*tmp);
if (mRegistryName == nsnull ||
mJarLocation == nsnull ||
mTargetFile == nsnull ||
mVersionInfo == nsnull )
{
*error = nsInstall::OUT_OF_MEMORY;
return;
}
mVersionInfo->Init(inVInfo);
if(! inPartialPath.Equals(""))
*mTargetFile += inPartialPath;
}
nsInstallPatch::~nsInstallPatch()
{
if (mVersionInfo)
delete mVersionInfo;
if (mTargetFile)
delete mTargetFile;
if (mJarLocation)
delete mJarLocation;
if (mRegistryName)
delete mRegistryName;
if (mPatchedFile)
delete mPatchedFile;
if (mPatchFile)
delete mPatchFile;
MOZ_COUNT_DTOR(nsInstallPatch);
}
PRInt32 nsInstallPatch::Prepare()
{
PRInt32 err;
PRBool deleteOldSrc;
if (mTargetFile == nsnull)
return nsInstall::INVALID_ARGUMENTS;
if (mTargetFile->Exists())
{
if (mTargetFile->IsFile())
{
err = nsInstall::SUCCESS;
}
else
{
err = nsInstall::IS_DIRECTORY;
}
}
else
{
err = nsInstall::DOES_NOT_EXIST;
}
if (err != nsInstall::SUCCESS)
{
return err;
}
err = mInstall->ExtractFileFromJar(*mJarLocation, mTargetFile, &mPatchFile);
nsFileSpec *fileName = nsnull;
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
mInstall->GetPatch(&ikey, &fileName);
if (fileName != nsnull)
{
deleteOldSrc = PR_TRUE;
}
else
{
fileName = mTargetFile;
deleteOldSrc = PR_FALSE;
}
err = NativePatch( *fileName, // the file to patch
*mPatchFile, // the patch that was extracted from the jarfile
&mPatchedFile); // the new patched file
// clean up extracted diff data file
if ( (mPatchFile != nsnull) && (mPatchFile->Exists()) )
{
mPatchFile->Delete(PR_FALSE);
}
if (err != nsInstall::SUCCESS)
{
// clean up tmp patched file since patching failed
if ((mPatchedFile != nsnull) && (mPatchedFile->Exists()))
{
mPatchedFile->Delete(PR_FALSE);
}
return err;
}
PR_ASSERT(mPatchedFile != nsnull);
mInstall->AddPatch(&ikey, mPatchedFile );
if ( deleteOldSrc )
{
DeleteFileNowOrSchedule(*fileName );
}
return err;
}
PRInt32 nsInstallPatch::Complete()
{
if ((mInstall == nsnull) || (mVersionInfo == nsnull) || (mPatchedFile == nsnull) || (mTargetFile == nsnull))
{
return nsInstall::INVALID_ARGUMENTS;
}
PRInt32 err = nsInstall::SUCCESS;
nsFileSpec *fileName = nsnull;
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
mInstall->GetPatch(&ikey, &fileName);
if (fileName == nsnull)
{
err = nsInstall::UNEXPECTED_ERROR;
}
else if (*fileName == *mPatchedFile)
{
// the patch has not been superceded--do final replacement
err = ReplaceFileNowOrSchedule( *mPatchedFile, *mTargetFile);
if ( 0 == err || nsInstall::REBOOT_NEEDED == err )
{
nsString tempVersionString;
mVersionInfo->ToString(tempVersionString);
char* tempRegName = mRegistryName->ToNewCString();
char* tempVersion = tempVersionString.ToNewCString();
// DO NOT propogate version registry errors, it will abort
// FinalizeInstall() leaving things hosed. These piddly errors
// aren't worth that.
VR_Install( tempRegName,
(char*)(const char *)mTargetFile->GetNativePathCString(),
tempVersion,
PR_FALSE );
if (tempRegName) Recycle(tempRegName);
if (tempVersion) Recycle(tempVersion);
}
else
{
err = nsInstall::UNEXPECTED_ERROR;
}
}
else
{
// nothing -- old intermediate patched file was
// deleted by a superceding patch
}
return err;
}
void nsInstallPatch::Abort()
{
nsFileSpec *fileName = nsnull;
nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );
mInstall->GetPatch(&ikey, &fileName);
if (fileName != nsnull && (*fileName == *mPatchedFile) )
{
DeleteFileNowOrSchedule( *mPatchedFile );
}
}
char* nsInstallPatch::toString()
{
char* buffer = new char[1024];
char* rsrcVal = nsnull;
if (buffer == nsnull || !mInstall)
return buffer;
if (mTargetFile != nsnull)
{
rsrcVal = mInstall->GetResourcedString("Patch");
if (rsrcVal)
{
sprintf( buffer, rsrcVal, mTargetFile->GetCString());
nsCRT::free(rsrcVal);
}
}
return buffer;
}
PRBool
nsInstallPatch::CanUninstall()
{
return PR_FALSE;
}
PRBool
nsInstallPatch::RegisterPackageNode()
{
return PR_TRUE;
}
PRInt32
nsInstallPatch::NativePatch(const nsFileSpec &sourceFile, const nsFileSpec &patchFile, nsFileSpec **newFile)
{
DIFFDATA *dd;
PRInt32 status = GDIFF_ERR_MEM;
char *tmpurl = NULL;
char *realfile = PL_strdup(nsNSPRPath(sourceFile)); // needs to be sourceFile!!!
nsFileSpec *outFileSpec = new nsFileSpec;
nsFileSpec *tempSrcFile = new nsFileSpec; // TODO: do you need to free?
if (!outFileSpec) {
status = GDIFF_ERR_MEM;
goto cleanup;
}
*outFileSpec = sourceFile;
dd = (DIFFDATA *)PR_Calloc( 1, sizeof(DIFFDATA));
if (dd != NULL)
{
dd->databuf = (uchar*)PR_Malloc(BUFSIZE);
if (dd->databuf == NULL)
{
status = GDIFF_ERR_MEM;
goto cleanup;
}
dd->bufsize = BUFSIZE;
// validate patch header & check for special instructions
dd->fDiff = PR_Open (nsNSPRPath(patchFile), PR_RDONLY, 0666);
if (dd->fDiff != NULL)
{
status = gdiff_parseHeader(dd);
} else {
status = GDIFF_ERR_ACCESS;
}
// in case we need to unbind Win32 images OR encode Mac file
if (( dd->bWin32BoundImage || dd->bMacAppleSingle) && (status == GDIFF_OK ))
{
// make an unique tmp file (FILENAME-src.EXT)
*tempSrcFile = sourceFile;
nsString tmpName = "-src";
nsString tmpFileName = sourceFile.GetLeafName();
PRInt32 i;
if ((i = tmpFileName.RFindChar('.')) > 0)
{
nsString ext;
nsString fName;
tmpFileName.Right(ext, (tmpFileName.Length() - i) );
tmpFileName.Left(fName, (tmpFileName.Length() - (tmpFileName.Length() - i)));
tmpFileName = fName + tmpName + ext;
} else {
tmpFileName += tmpName;
}
tempSrcFile->SetLeafName(tmpFileName);
tempSrcFile->MakeUnique();
#ifdef WIN32
// unbind Win32 images
char *tmpFile = PL_strdup(nsNSPRPath(*tempSrcFile));
if (su_unbind(realfile, tmpFile))
{
PL_strfree(realfile);
realfile = PL_strdup(tmpFile);
}
else
{
status = GDIFF_ERR_MEM;
}
PL_strfree(tmpFile);
#endif
#ifdef XP_MAC
// Encode src file, and put into temp file
FSSpec sourceSpec = sourceFile.GetFSSpec();
FSSpec tempSpec = tempSrcFile->GetFSSpec();
status = PAS_EncodeFile(&sourceSpec, &tempSpec);
if (status == noErr)
{
// set
PL_strfree(realfile);
realfile = PL_strdup(nsNSPRPath(*tempSrcFile));
}
#endif
}
if (status != NS_OK)
goto cleanup;
// make a unique file at the same location of our source file (FILENAME-ptch.EXT)
nsString patchFileName = "-ptch";
nsString newFileName = sourceFile.GetLeafName();
PRInt32 index;
if ((index = newFileName.RFindChar('.')) > 0)
{
nsString extention;
nsString fileName;
newFileName.Right(extention, (newFileName.Length() - index) );
newFileName.Left(fileName, (newFileName.Length() - (newFileName.Length() - index)));
newFileName = fileName + patchFileName + extention;
} else {
newFileName += patchFileName;
}
outFileSpec->SetLeafName(newFileName);
outFileSpec->MakeUnique();
char *outFile = PL_strdup(nsNSPRPath(*outFileSpec));
// apply patch to the source file
dd->fSrc = PR_Open ( realfile, PR_RDONLY, 0666);
dd->fOut = PR_Open ( outFile, PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0666);
if (dd->fSrc != NULL && dd->fOut != NULL)
{
status = gdiff_validateFile (dd, SRCFILE);
// specify why diff failed
if (status == GDIFF_ERR_CHECKSUM)
status = GDIFF_ERR_CHECKSUM_TARGET;
if (status == GDIFF_OK)
status = gdiff_ApplyPatch(dd);
if (status == GDIFF_OK)
status = gdiff_validateFile (dd, OUTFILE);
if (status == GDIFF_ERR_CHECKSUM)
status = GDIFF_ERR_CHECKSUM_RESULT;
*newFile = outFileSpec;
if ( outFile != nsnull)
PL_strfree( outFile );
} else {
status = GDIFF_ERR_ACCESS;
}
}
#ifdef XP_MAC
if ( dd->bMacAppleSingle && status == GDIFF_OK )
{
// create another file, so that we can decode somewhere
nsFileSpec anotherName = *outFileSpec;
anotherName.MakeUnique();
// Close the out file so that we can read it
PR_Close( dd->fOut );
dd->fOut = NULL;
FSSpec outSpec = outFileSpec->GetFSSpec();
FSSpec anotherSpec = anotherName.GetFSSpec();
if ( outFileSpec->Exists() )
{
printf("filesize: %d\n", outFileSpec->GetFileSize());
}
status = PAS_DecodeFile(&outSpec, &anotherSpec);
if (status != noErr)
{
goto cleanup;
}
nsFileSpec parent;
outFileSpec->GetParent(parent);
outFileSpec->Delete(PR_FALSE);
anotherName.CopyToDir(parent);
*outFileSpec = anotherName;
*newFile = outFileSpec;
}
#endif
cleanup:
if ( dd != NULL )
{
if ( dd->fSrc != nsnull )
PR_Close( dd->fSrc );
if ( dd->fDiff != nsnull )
PR_Close( dd->fDiff );
if ( dd->fOut != nsnull )
{
PR_Close( dd->fOut );
}
if ( status != GDIFF_OK )
//XP_FileRemove( outfile, outtype );
newFile = NULL;
PR_FREEIF( dd->databuf );
PR_FREEIF( dd->oldChecksum );
PR_FREEIF( dd->newChecksum );
PR_DELETE(dd);
}
if ( tmpurl != NULL ) {
//XP_FileRemove( tmpurl, xpURL );
tmpurl = NULL;
PR_DELETE( tmpurl );
}
if (realfile != NULL)
{
PL_strfree(realfile);
}
if ((tempSrcFile != nsnull) && (tempSrcFile->Exists()) )
{
tempSrcFile->Delete(PR_FALSE);
}
/* lets map any GDIFF error to nice SU errors */
switch (status)
{
case GDIFF_OK:
break;
case GDIFF_ERR_HEADER:
case GDIFF_ERR_BADDIFF:
case GDIFF_ERR_OPCODE:
case GDIFF_ERR_CHKSUMTYPE:
status = nsInstall::PATCH_BAD_DIFF;
break;
case GDIFF_ERR_CHECKSUM_TARGET:
status = nsInstall::PATCH_BAD_CHECKSUM_TARGET;
break;
case GDIFF_ERR_CHECKSUM_RESULT:
status = nsInstall::PATCH_BAD_CHECKSUM_RESULT;
break;
case GDIFF_ERR_OLDFILE:
case GDIFF_ERR_ACCESS:
case GDIFF_ERR_MEM:
case GDIFF_ERR_UNKNOWN:
default:
status = nsInstall::UNEXPECTED_ERROR;
break;
}
return status;
// return -1; //old return value
}
void*
nsInstallPatch::HashFilePath(const nsFilePath& aPath)
{
PRUint32 rv = 0;
char* cPath = PL_strdup(nsNSPRPath(aPath));
if(cPath != nsnull)
{
char ch;
char* pathIndex = cPath;
while ((ch = *pathIndex++) != 0)
{
// FYI: rv = rv*37 + ch
rv = ((rv << 5) + (rv << 2) + rv) + ch;
}
}
PL_strfree(cPath);
return (void*)rv;
}
/*---------------------------------------------------------
* gdiff_parseHeader()
*
* reads and validates the GDIFF header info
*---------------------------------------------------------
*/
static
int32 gdiff_parseHeader( pDIFFDATA dd )
{
int32 err = GDIFF_OK;
uint8 cslen;
uint8 oldcslen;
uint8 newcslen;
uint32 nRead;
uchar header[GDIFF_HEADERSIZE];
/* Read the fixed-size part of the header */
nRead = PR_Read (dd->fDiff, header, GDIFF_HEADERSIZE);
if ( nRead != GDIFF_HEADERSIZE ||
memcmp( header, GDIFF_MAGIC, GDIFF_MAGIC_LEN ) != 0 ||
header[GDIFF_VER_POS] != GDIFF_VER )
{
err = GDIFF_ERR_HEADER;
}
else
{
/* get the checksum information */
dd->checksumType = header[GDIFF_CS_POS];
cslen = header[GDIFF_CSLEN_POS];
if ( cslen > 0 )
{
oldcslen = cslen / 2;
newcslen = cslen - oldcslen;
PR_ASSERT( newcslen == oldcslen );
dd->checksumLength = oldcslen;
dd->oldChecksum = (uchar*)PR_MALLOC(oldcslen);
dd->newChecksum = (uchar*)PR_MALLOC(newcslen);
if ( dd->oldChecksum != NULL && dd->newChecksum != NULL )
{
nRead = PR_Read (dd->fDiff, dd->oldChecksum, oldcslen);
if ( nRead == oldcslen )
{
nRead = PR_Read (dd->fDiff, dd->newChecksum, newcslen);
if ( nRead != newcslen ) {
err = GDIFF_ERR_HEADER;
}
}
else {
err = GDIFF_ERR_HEADER;
}
}
else {
err = GDIFF_ERR_MEM;
}
}
/* get application data, if any */
if ( err == GDIFF_OK )
{
uint32 appdataSize;
uchar *buf;
uchar lenbuf[GDIFF_APPDATALEN];
nRead = PR_Read(dd->fDiff, lenbuf, GDIFF_APPDATALEN);
if ( nRead == GDIFF_APPDATALEN )
{
appdataSize = getlong(lenbuf);
if ( appdataSize > 0 )
{
buf = (uchar *)PR_MALLOC( appdataSize );
if ( buf != NULL )
{
nRead = PR_Read (dd->fDiff, buf, appdataSize);
if ( nRead == appdataSize )
{
if ( 0 == memcmp( buf, APPFLAG_W32BOUND, appdataSize ) )
dd->bWin32BoundImage = TRUE;
if ( 0 == memcmp( buf, APPFLAG_APPLESINGLE, appdataSize ) )
dd->bMacAppleSingle = TRUE;
}
else {
err = GDIFF_ERR_HEADER;
}
PR_DELETE( buf );
}
else {
err = GDIFF_ERR_MEM;
}
}
}
else {
err = GDIFF_ERR_HEADER;
}
}
}
return (err);
}
/*---------------------------------------------------------
* gdiff_validateFile()
*
* computes the checksum of the file and compares it to
* the value stored in the GDIFF header
*---------------------------------------------------------
*/
static
int32 gdiff_validateFile( pDIFFDATA dd, int file )
{
int32 result;
PRFileDesc* fh;
uchar* chksum;
/* which file are we dealing with? */
if ( file == SRCFILE ) {
fh = dd->fSrc;
chksum = dd->oldChecksum;
}
else { /* OUTFILE */
fh = dd->fOut;
chksum = dd->newChecksum;
}
/* make sure file's at beginning */
PR_Seek( fh, 0, PR_SEEK_SET );
/* calculate appropriate checksum */
switch (dd->checksumType)
{
case GDIFF_CS_NONE:
result = GDIFF_OK;
break;
case GDIFF_CS_CRC32:
if ( dd->checksumLength == CRC32_LEN )
result = gdiff_valCRC32( dd, fh, getlong(chksum) );
else
result = GDIFF_ERR_HEADER;
break;
case GDIFF_CS_MD5:
case GDIFF_CS_SHA:
default:
/* unsupported checksum type */
result = GDIFF_ERR_CHKSUMTYPE;
break;
}
/* reset file position to beginning and return status */
PR_Seek( fh, 0, PR_SEEK_SET );
return (result);
}
/*---------------------------------------------------------
* gdiff_valCRC32()
*
* computes the checksum of the file and compares it to
* the passed in checksum. Assumes file is positioned at
* beginning.
*---------------------------------------------------------
*/
static
int32 gdiff_valCRC32( pDIFFDATA dd, PRFileDesc* fh, uint32 chksum )
{
uint32 crc;
uint32 nRead;
crc = crc32(0L, Z_NULL, 0);
nRead = PR_Read (fh, dd->databuf, dd->bufsize);
while ( nRead > 0 )
{
crc = crc32( crc, dd->databuf, nRead );
nRead = PR_Read (fh, dd->databuf, dd->bufsize);
}
if ( crc == chksum )
return GDIFF_OK;
else
return GDIFF_ERR_CHECKSUM;
}
/*---------------------------------------------------------
* gdiff_ApplyPatch()
*
* Combines patch data with source file to produce the
* new target file. Assumes all three files have been
* opened, GDIFF header read, and all other setup complete
*
* The GDIFF patch is processed sequentially which random
* access is neccessary for the source file.
*---------------------------------------------------------
*/
static
int32 gdiff_ApplyPatch( pDIFFDATA dd )
{
int32 err;
PRBool done;
uint32 position;
uint32 count;
uchar opcode;
uchar cmdbuf[MAXCMDSIZE];
done = FALSE;
while ( !done ) {
err = gdiff_getdiff( dd, &opcode, OPSIZE );
if ( err != GDIFF_OK )
break;
switch (opcode)
{
case ENDDIFF:
done = TRUE;
break;
case ADD16:
err = gdiff_getdiff( dd, cmdbuf, ADD16SIZE );
if ( err == GDIFF_OK ) {
err = gdiff_add( dd, getshort( cmdbuf ) );
}
break;
case ADD32:
err = gdiff_getdiff( dd, cmdbuf, ADD32SIZE );
if ( err == GDIFF_OK ) {
err = gdiff_add( dd, getlong( cmdbuf ) );
}
break;
case COPY16BYTE:
err = gdiff_getdiff( dd, cmdbuf, COPY16BYTESIZE );
if ( err == GDIFF_OK ) {
position = getshort( cmdbuf );
count = *(cmdbuf + sizeof(short));
err = gdiff_copy( dd, position, count );
}
break;
case COPY16SHORT:
err = gdiff_getdiff( dd, cmdbuf, COPY16SHORTSIZE );
if ( err == GDIFF_OK ) {
position = getshort( cmdbuf );
count = getshort(cmdbuf + sizeof(short));
err = gdiff_copy( dd, position, count );
}
break;
case COPY16LONG:
err = gdiff_getdiff( dd, cmdbuf, COPY16LONGSIZE );
if ( err == GDIFF_OK ) {
position = getshort( cmdbuf );
count = getlong(cmdbuf + sizeof(short));
err = gdiff_copy( dd, position, count );
}
break;
case COPY32BYTE:
err = gdiff_getdiff( dd, cmdbuf, COPY32BYTESIZE );
if ( err == GDIFF_OK ) {
position = getlong( cmdbuf );
count = *(cmdbuf + sizeof(long));
err = gdiff_copy( dd, position, count );
}
break;
case COPY32SHORT:
err = gdiff_getdiff( dd, cmdbuf, COPY32SHORTSIZE );
if ( err == GDIFF_OK ) {
position = getlong( cmdbuf );
count = getshort(cmdbuf + sizeof(long));
err = gdiff_copy( dd, position, count );
}
break;
case COPY32LONG:
err = gdiff_getdiff( dd, cmdbuf, COPY32LONGSIZE );
if ( err == GDIFF_OK ) {
position = getlong( cmdbuf );
count = getlong(cmdbuf + sizeof(long));
err = gdiff_copy( dd, position, count );
}
break;
case COPY64:
/* we don't support 64-bit file positioning yet */
err = GDIFF_ERR_OPCODE;
break;
default:
err = gdiff_add( dd, opcode );
break;
}
if ( err != GDIFF_OK )
done = TRUE;
}
/* return status */
return (err);
}
/*---------------------------------------------------------
* gdiff_getdiff()
*
* reads the next "length" bytes of the diff into "buffer"
*
* XXX: need a diff buffer to optimize reads!
*---------------------------------------------------------
*/
static
int32 gdiff_getdiff( pDIFFDATA dd, uchar *buffer, uint32 length )
{
uint32 bytesRead;
bytesRead = PR_Read (dd->fDiff, buffer, length);
if ( bytesRead != length )
return GDIFF_ERR_BADDIFF;
return GDIFF_OK;
}
/*---------------------------------------------------------
* gdiff_add()
*
* append "count" bytes from diff file to new file
*---------------------------------------------------------
*/
static
int32 gdiff_add( pDIFFDATA dd, uint32 count )
{
int32 err = GDIFF_OK;
uint32 nRead;
uint32 chunksize;
while ( count > 0 ) {
chunksize = ( count > dd->bufsize) ? dd->bufsize : count;
nRead = PR_Read (dd->fDiff, dd->databuf, chunksize);
if ( nRead != chunksize ) {
err = GDIFF_ERR_BADDIFF;
break;
}
PR_Write (dd->fOut, dd->databuf, chunksize);
count -= chunksize;
}
return (err);
}
/*---------------------------------------------------------
* gdiff_copy()
*
* copy "count" bytes from "position" in source file
*---------------------------------------------------------
*/
static
int32 gdiff_copy( pDIFFDATA dd, uint32 position, uint32 count )
{
int32 err = GDIFF_OK;
uint32 nRead;
uint32 chunksize;
PR_Seek (dd->fSrc, position, PR_SEEK_SET);
while ( count > 0 ) {
chunksize = (count > dd->bufsize) ? dd->bufsize : count;
nRead = PR_Read (dd->fSrc, dd->databuf, chunksize);
if ( nRead != chunksize ) {
err = GDIFF_ERR_OLDFILE;
break;
}
PR_Write (dd->fOut, dd->databuf, chunksize);
count -= chunksize;
}
return (err);
}
#ifdef WIN32
#include <winfile.h>
/*---------------------------------------------------------
* su_unbind()
*
* strips import binding information from Win32
* executables and .DLL's
*---------------------------------------------------------
*/
static
PRBool su_unbind(char* oldfile, char* newfile)
{
PRBool bSuccess = FALSE;
int i;
DWORD nRead;
PDWORD pOrigThunk;
PDWORD pBoundThunk;
FILE *fh = NULL;
char *buf;
BOOL bModified = FALSE;
BOOL bImports = FALSE;
IMAGE_DOS_HEADER mz;
IMAGE_NT_HEADERS nt;
IMAGE_SECTION_HEADER sec;
PIMAGE_DATA_DIRECTORY pDir;
PIMAGE_IMPORT_DESCRIPTOR pImp;
typedef BOOL (__stdcall *BINDIMAGEEX)(DWORD Flags,
LPSTR ImageName,
LPSTR DllPath,
LPSTR SymbolPath,
PVOID StatusRoutine);
HINSTANCE hImageHelp;
BINDIMAGEEX pfnBindImageEx;
if ( oldfile != NULL && newfile != NULL &&
CopyFile( oldfile, newfile, FALSE ) )
{
/* call BindImage() first to make maximum room for a possible
* NT-style Bound Import Descriptors which can change various
* offsets in the file */
hImageHelp = LoadLibrary("IMAGEHLP.DLL");
if ( hImageHelp > (HINSTANCE)HINSTANCE_ERROR ) {
pfnBindImageEx = (BINDIMAGEEX)GetProcAddress(hImageHelp, "BindImageEx");
if (pfnBindImageEx) {
pfnBindImageEx(0, newfile, NULL, NULL, NULL);
}
FreeLibrary(hImageHelp);
}
fh = fopen( newfile, "r+b" );
if ( fh == NULL )
goto bail;
/* read and validate the MZ header */
nRead = fread( &mz, 1, sizeof(mz), fh );
if ( nRead != sizeof(mz) || mz.e_magic != IMAGE_DOS_SIGNATURE )
goto bail;
/* read and validate the NT header */
fseek( fh, mz.e_lfanew, SEEK_SET );
nRead = fread( &nt, 1, sizeof(nt), fh );
if ( nRead != sizeof(nt) ||
nt.Signature != IMAGE_NT_SIGNATURE ||
nt.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
{
goto bail;
}
/* find .idata section */
for (i = nt.FileHeader.NumberOfSections; i > 0; i--)
{
nRead = fread( &sec, 1, sizeof(sec), fh );
if ( nRead != sizeof(sec) )
goto bail;
if ( memcmp( sec.Name, ".idata", 6 ) == 0 ) {
bImports = TRUE;
break;
}
}
/* Zap any binding in the imports section */
if ( bImports )
{
buf = (char*)malloc( sec.SizeOfRawData );
if ( buf == NULL )
goto bail;
fseek( fh, sec.PointerToRawData, SEEK_SET );
nRead = fread( buf, 1, sec.SizeOfRawData, fh );
if ( nRead != sec.SizeOfRawData ) {
free( buf );
goto bail;
}
pImp = (PIMAGE_IMPORT_DESCRIPTOR)buf;
while ( pImp->OriginalFirstThunk != 0 )
{
if ( pImp->TimeDateStamp != 0 || pImp->ForwarderChain != 0 )
{
/* found a bound .DLL */
pImp->TimeDateStamp = 0;
pImp->ForwarderChain = 0;
bModified = TRUE;
pOrigThunk = (PDWORD)(buf + (DWORD)(pImp->OriginalFirstThunk) - sec.VirtualAddress);
pBoundThunk = (PDWORD)(buf + (DWORD)(pImp->FirstThunk) - sec.VirtualAddress);
for ( ; *pOrigThunk != 0; pOrigThunk++, pBoundThunk++ ) {
*pBoundThunk = *pOrigThunk;
}
}
pImp++;
}
if ( bModified )
{
/* it's been changed, write out the section */
fseek( fh, sec.PointerToRawData, SEEK_SET );
fwrite( buf, 1, sec.SizeOfRawData, fh );
}
free( buf );
}
/* Check for a Bound Import Directory in the headers */
pDir = &nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT];
if ( pDir->VirtualAddress != 0 )
{
/* we've got one, so stomp it */
buf = (char*)calloc( pDir->Size, 1 );
if ( buf == NULL )
goto bail;
fseek( fh, pDir->VirtualAddress, SEEK_SET );
fwrite( buf, pDir->Size, 1, fh );
free( buf );
pDir->VirtualAddress = 0;
pDir->Size = 0;
bModified = TRUE;
}
/* Write out changed headers if necessary */
if ( bModified )
{
/* zap checksum since it's now invalid */
nt.OptionalHeader.CheckSum = 0;
fseek( fh, mz.e_lfanew, SEEK_SET );
fwrite( &nt, 1, sizeof(nt), fh );
}
bSuccess = TRUE;
}
bail:
if ( fh != NULL )
fclose(fh);
return bSuccess;
}
#endif