/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla 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/MPL/ * * 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 of 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 MPL, 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 MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "zlib.h" #include "nsCRT.h" #include "prmem.h" #include "nsXPIDLString.h" #include "nsInstall.h" #include "nsInstallPatch.h" #include "nsInstallResources.h" #include "nsIDOMInstallVersion.h" #include "nsILocalFile.h" #include "nsNativeCharsetUtils.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 #ifdef XP_WIN #include #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; } nsCOMPtr tmp; NS_NewNativeLocalFile(nsDependentCString(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 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(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, 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 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 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, 0); if ( 0 == err || nsInstall::REBOOT_NEEDED == err ) { nsString tempVersionString; mVersionInfo->ToString(tempVersionString); nsCAutoString tempPath; mTargetFile->GetNativePath(tempPath); // DO NOT propogate 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 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) { nsCAutoString temp; mTargetFile->GetNativePath(temp); sprintf( buffer, rsrcVal, temp.get()); 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 outFileSpec; nsCOMPtr tempSrcFile; nsCOMPtr uniqueSrcFile; nsCOMPtr patchFileLocal = do_QueryInterface(patchFile, &rv); nsCAutoString realfile; sourceFile->GetNativePath(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 // we're just reading, the 0400 is an annotation. patchFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0400, &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) nsAutoString leafName; rv = sourceFile->GetLeafName(leafName); NS_NAMED_LITERAL_STRING(tmpName, "-src"); PRInt32 i; if ((i = leafName.RFindChar('.')) > 0) { // build the temp filename for which to unbind the file to. // eg: if the filename is bind.dll, the temp src filename would // be bind-src.dll nsAutoString ext; nsAutoString fName; // get the extension leafName.Right(ext, (leafName.Length() - i) ); // get the filename - extension leafName.Left(fName, (leafName.Length() - (leafName.Length() - i))); // build the temp filename with '-src' at the end of filename, // but before extension leafName.Assign(fName); leafName.Append(tmpName); leafName.Append(ext); } else { // no extension found, just append '-src' to the end of filename leafName.Append(tmpName); } rv = sourceFile->Clone(getter_AddRefs(tempSrcFile)); //Clone the sourceFile tempSrcFile->SetLeafName(leafName); //Append the new leafname uniqueSrcFile = do_QueryInterface(tempSrcFile, &rv); uniqueSrcFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); #ifdef WIN32 // unbind Win32 images nsCAutoString unboundFile; uniqueSrcFile->GetNativePath(unboundFile); if (su_unbind(NS_CONST_CAST(char*, realfile.get()), NS_CONST_CAST(char*,unboundFile.get()))) // { // un-binding worked, save the tmp name for later uniqueSrcFile->GetNativePath(realfile); } else { status = GDIFF_ERR_MEM; } unboundFile.Truncate(); #endif #ifdef XP_MAC // Encode src file, and put into temp file FSSpec sourceSpec, tempSpec; nsCOMPtr tempSourceFile; tempSourceFile = do_QueryInterface(sourceFile, &rv); tempSourceFile->GetFSSpec(&sourceSpec); status = PAS_EncodeFile(&sourceSpec, &tempSpec); if (status == noErr) { // set tempSrcFile->GetNativePath(realfile); } #endif } if (status != NS_OK) goto cleanup; // make a unique file at the same location of our source file (FILENAME-ptch.EXT) NS_NAMED_LITERAL_STRING(patchFileName, "-ptch"); nsAutoString newFileName; sourceFile->GetLeafName(newFileName); PRInt32 index; if ((index = newFileName.RFindChar('.')) > 0) { nsAutoString extention; nsAutoString 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); //Set new leafname nsCOMPtr outFileLocal = do_QueryInterface(outFileSpec, &rv); outFileLocal->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); // 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 realFileLocal = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);; realFileLocal->InitWithNativePath(realfile); // it's ok for people in the group to modify these files later, but it wouldn't be good for just anyone. realFileLocal->OpenNSPRFileDesc(PR_RDONLY, 0664, &dd->fSrc); outFileLocal->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664, &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 anotherName; nsCOMPtr bsTemp; outFileSpec->Clone(getter_AddRefs(bsTemp)); //Clone because we'll be changing the name anotherName = do_QueryInterface(bsTemp, &rv); //Set the old name anotherName->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); // Close the out file so that we can read it PR_Close( dd->fOut ); dd->fOut = NULL; FSSpec outSpec; FSSpec anotherSpec; nsCOMPtr outSpecMacSpecific; nsCOMPtr 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 parent; outFileSpec->GetParent(getter_AddRefs(parent)); outFileSpec->Remove(PR_FALSE); nsAutoString 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 (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; nsCAutoString cPath; aPath->GetNativePath(cPath); if (!cPath.IsEmpty()) { char ch; const char* pathIndex = cPath.get(); while ((ch = *pathIndex++) != 0) { // FYI: rv = rv*37 + ch rv = ((rv << 5) + (rv << 2) + rv) + ch; } } 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 necessary 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