pjs/xpinstall/src/nsInstallPatch.cpp

1322 строки
36 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "zlib.h"
#include "nsFileSpec.h"
#include "prmem.h"
#include "nsXPIDLString.h"
#include "nsInstall.h"
#include "nsInstallPatch.h"
#include "nsInstallResources.h"
#include "nsIDOMInstallVersion.h"
#include "gdiff.h"
#include "VerReg.h"
#include "ScheduledTasks.h"
#include "plstr.h"
#include "prlog.h"
#ifdef XP_MAC
#include "PatchableAppleSingle.h"
#include "nsILocalFileMac.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];
PRInt32 err = VR_GetPath( NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(inVRName).get()),
sizeof(tempTargetFile), tempTargetFile );
if (err != REGERR_OK)
{
*error = nsInstall::NO_SUCH_COMPONENT;
return;
}
nsString folderSpec; folderSpec.AssignWithConversion(tempTargetFile);
nsCOMPtr<nsILocalFile> tmp;
NS_NewLocalFile((char*)tempTargetFile, PR_TRUE, getter_AddRefs(tmp));
mPatchFile = nsnull;
mTargetFile = nsnull;
mPatchedFile = nsnull;
mRegistryName = new nsString(inVRName);
mJarLocation = new nsString(inJarLocation);
mVersionInfo = new nsInstallVersion();
tmp->Clone(getter_AddRefs(mTargetFile));
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.IsEmpty()) || (inJarLocation.IsEmpty()))
{
*error = nsInstall::INVALID_ARGUMENTS;
return;
}
nsCOMPtr<nsIFile> 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();
tmp->Clone(getter_AddRefs(mTargetFile));
if (mRegistryName == nsnull ||
mJarLocation == nsnull ||
mTargetFile == nsnull ||
mVersionInfo == nsnull )
{
*error = nsInstall::OUT_OF_MEMORY;
return;
}
mVersionInfo->Init(inVInfo);
if(! inPartialPath.IsEmpty())
mTargetFile->Append(NS_LossyConvertUCS2toASCII(inPartialPath).get());
}
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, flagExists, flagIsFile;
if (mTargetFile == nsnull)
return nsInstall::INVALID_ARGUMENTS;
mTargetFile->Exists(&flagExists);
if (flagExists)
{
mTargetFile->IsFile(&flagIsFile);
if (flagIsFile)
{
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, getter_AddRefs(mPatchFile));
nsCOMPtr<nsIFile> fileName = nsnull;
//nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );//nsIFileXXX: nsFilePath?
nsVoidKey ikey( HashFilePath( mTargetFile ));
mInstall->GetPatch(&ikey, getter_AddRefs(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
getter_AddRefs(mPatchedFile)); // the new patched file
// clean up extracted diff data file
mPatchFile->Exists(&flagExists);
if ( (mPatchFile != nsnull) && (flagExists) )
{
mPatchFile->Remove(PR_FALSE);
}
if (err != nsInstall::SUCCESS)
{
// clean up tmp patched file since patching failed
mPatchFile->Exists(&flagExists);
if ((mPatchedFile != nsnull) && (flagExists))
{
mPatchedFile->Remove(PR_FALSE);
}
return err;
}
PR_ASSERT(mPatchedFile != nsnull);
mInstall->AddPatch(&ikey, mPatchedFile );
if ( deleteOldSrc )
{
DeleteFileNowOrSchedule(fileName );
}
return err;
}
PRInt32 nsInstallPatch::Complete()
{
PRBool flagEquals;
if ((mInstall == nsnull) || (mVersionInfo == nsnull) || (mPatchedFile == nsnull) || (mTargetFile == nsnull))
{
return nsInstall::INVALID_ARGUMENTS;
}
PRInt32 err = nsInstall::SUCCESS;
nsCOMPtr<nsIFile> fileName = nsnull;
//nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) );//nsIFileXXX: nsFilePath?
nsVoidKey ikey( HashFilePath( mTargetFile ));
mInstall->GetPatch(&ikey, getter_AddRefs(fileName));
if (fileName == nsnull)
{
err = nsInstall::UNEXPECTED_ERROR;
}
else
{
fileName->Equals(mPatchedFile, &flagEquals);
if (flagEquals)
{
// 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);
nsXPIDLCString tempPath;
mTargetFile->GetPath(getter_Copies(tempPath));
// DO NOT propagate version registry errors, it will abort
// FinalizeInstall() leaving things hosed. These piddly errors
// aren't worth that.
VR_Install( NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(*mRegistryName).get()),
NS_CONST_CAST(char *, tempPath.get()),
NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(tempVersionString).get()),
PR_FALSE );
}
else
{
err = nsInstall::UNEXPECTED_ERROR;
}
}
else
{
// nothing -- old intermediate patched file was
// deleted by a superceding patch
}
}
return err;
}
void nsInstallPatch::Abort()
{
PRBool flagEquals;
nsCOMPtr<nsIFile> fileName = nsnull;
//nsVoidKey ikey( HashFilePath( nsFilePath(*mTargetFile) ) ); //nsIFileXXX: nsFilePath?
nsVoidKey ikey( HashFilePath( mTargetFile ));
mInstall->GetPatch(&ikey, getter_AddRefs(fileName));
fileName->Equals(mPatchedFile, &flagEquals);
if (fileName != nsnull && (flagEquals) )
{
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(NS_LITERAL_STRING("Patch"));
if (rsrcVal)
{
char* temp;
mTargetFile->GetPath(&temp);
sprintf( buffer, rsrcVal, temp);
nsCRT::free(rsrcVal);
}
}
return buffer;
}
PRBool
nsInstallPatch::CanUninstall()
{
return PR_FALSE;
}
PRBool
nsInstallPatch::RegisterPackageNode()
{
return PR_TRUE;
}
PRInt32
nsInstallPatch::NativePatch(nsIFile *sourceFile, nsIFile *patchFile, nsIFile **newFile) //nsIFileXXX: changed & to *
{
PRBool flagExists;
nsresult rv;
DIFFDATA *dd;
PRInt32 status = GDIFF_ERR_MEM;
char *tmpurl = NULL;
//nsFileSpec *outFileSpec = new nsFileSpec;
//nsFileSpec *tempSrcFile = new nsFileSpec; // TODO: do you need to free?
nsCOMPtr<nsIFile> outFileSpec;
nsCOMPtr<nsIFile> tempSrcFile;
nsCOMPtr<nsILocalFile> uniqueSrcFile;
nsCOMPtr<nsILocalFile> patchFileLocal = do_QueryInterface(patchFile, &rv);
char* realfile;
sourceFile->GetPath(&realfile);
sourceFile->Clone(getter_AddRefs(outFileSpec));
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
patchFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0666, &dd->fDiff);
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)
char* leafName;
rv = sourceFile->GetLeafName(&leafName);
nsString tmpName(NS_LITERAL_STRING("-src"));
nsString tmpFileName; tmpFileName.AssignWithConversion(leafName);
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;
}
rv = sourceFile->Clone(getter_AddRefs(tempSrcFile)); //Clone the sourceFile
tempSrcFile->SetLeafName(NS_LossyConvertUCS2toASCII(tmpFileName).get()); //Append the new leafname
uniqueSrcFile = do_QueryInterface(tempSrcFile, &rv); //Create an nsILocalFile version to pass to MakeUnique
MakeUnique(uniqueSrcFile);
char* realfile;
sourceFile->GetPath(&realfile);
#ifdef WIN32
// unbind Win32 images
char* unboundFile;
uniqueSrcFile->GetPath(&unboundFile);
if (su_unbind(realfile, unboundFile)) //
{
PL_strfree(realfile);
realfile = PL_strdup(unboundFile);
}
else
{
status = GDIFF_ERR_MEM;
}
PL_strfree(unboundFile);
#endif
#ifdef XP_MAC
// Encode src file, and put into temp file
FSSpec sourceSpec, tempSpec;
nsCOMPtr<nsILocalFileMac> tempSourceFile;
tempSourceFile = do_QueryInterface(sourceFile, &rv);
tempSourceFile->GetFSSpec(&sourceSpec);
status = PAS_EncodeFile(&sourceSpec, &tempSpec);
if (status == noErr)
{
// set
PL_strfree(realfile);
tempSrcFile->GetPath(&realfile);
}
#endif
}
if (status != NS_OK)
goto cleanup;
// make a unique file at the same location of our source file (FILENAME-ptch.EXT)
nsString patchFileName(NS_LITERAL_STRING("-ptch"));
char* leafName;
sourceFile->GetLeafName(&leafName);
nsString newFileName; newFileName.AssignWithConversion(leafName);;
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(NS_LossyConvertUCS2toASCII(newFileName).get()); //Set new leafname
nsCOMPtr<nsILocalFile> outFileLocal = do_QueryInterface(outFileSpec, &rv); //Create an nsILocalFile version
//to send to MakeUnique()
MakeUnique(outFileLocal);
// 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);
nsCOMPtr<nsILocalFile> realFileLocal = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);;
realFileLocal->InitWithPath(realfile);
realFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0666, &dd->fSrc);
outFileLocal->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0666, &dd->fOut);
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;
rv = outFileSpec->Clone(newFile);
}
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;
nsCOMPtr<nsILocalFile> anotherName;
nsCOMPtr<nsIFile> bsTemp;
outFileSpec->Clone(getter_AddRefs(bsTemp)); //Clone because we'll be changing the name
anotherName = do_QueryInterface(bsTemp, &rv); //Set the old name
MakeUnique(anotherName); //Now give it the new name
// Close the out file so that we can read it
PR_Close( dd->fOut );
dd->fOut = NULL;
FSSpec outSpec;
FSSpec anotherSpec;
nsCOMPtr<nsILocalFileMac> outSpecMacSpecific;
nsCOMPtr<nsILocalFileMac> anotherNameMacSpecific;
anotherNameMacSpecific = do_QueryInterface(anotherName, &rv); //set value to nsILocalFileMac (sheesh)
outSpecMacSpecific = do_QueryInterface(outFileSpec, &rv); //ditto
anotherNameMacSpecific->GetFSSpec(&anotherSpec);
outSpecMacSpecific->GetFSSpec(&outSpec);
outFileSpec->Exists(&flagExists);
if ( flagExists )
{
PRInt64 fileSize;
outFileSpec->GetFileSize(&fileSize);
}
status = PAS_DecodeFile(&outSpec, &anotherSpec);
if (status != noErr)
{
goto cleanup;
}
nsCOMPtr<nsIFile> parent;
outFileSpec->GetParent(getter_AddRefs(parent));
outFileSpec->Remove(PR_FALSE);
char* leaf;
anotherName->GetLeafName(&leaf);
anotherName->CopyTo(parent, leaf);
anotherName->Clone(newFile);
}
#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)
{
tempSrcFile->Exists(&flagExists);
if (flagExists)
tempSrcFile->Remove(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(nsIFile* aPath)
{
PRUint32 rv = 0;
char* cPath;
aPath->GetPath(&cPath);
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
/*---------------------------------------------------------
* 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