Add ZIP_TestArchive support for CRC32 verification of all or individual items.

b = 61544
 r = dveditz
sr = alecf
This commit is contained in:
sgehani%netscape.com 2000-12-27 07:05:55 +00:00
parent 0dcaf7b61a
commit 6d76941848
5 changed files with 279 additions and 7 deletions

View File

@ -64,13 +64,21 @@ interface nsIZipReader : nsISupports
/**
* Opens a zip reader.
*/
void open();
void open();
/**
* Closes a zip reader. Subsequent attempts to extract files or read from
* its input stream will result in an error.
*/
void close();
void close();
/**
* Tests the integrity of the archive by performing a CRC check
* on each item expanded into memory. If an entry is specified
* the integrity of only that item is tested. If NULL is passed
* in the inetgrity of all items in the archive are tested.
*/
void test(in string aEntryName);
/**
* Extracts a zip entry into a local file specified by outFile.
@ -85,7 +93,7 @@ interface nsIZipReader : nsISupports
/**
* Returns a simple enumerator whose elements are of type nsIZipEntry.
*/
nsISimpleEnumerator/*<nsIZipEntry>*/ findEntries(in string aPattern);
nsISimpleEnumerator/*<nsIZipEntry>*/ findEntries(in string aPattern);
/**
* Returns an input stream containing the contents of the specified zip entry.

View File

@ -242,6 +242,13 @@ nsJAR::Close()
return ziperr2nsresult(err);
}
NS_IMETHODIMP
nsJAR::Test(const char *aEntryName)
{
PRInt32 err = mZip.Test(aEntryName);
return ziperr2nsresult(err);
}
NS_IMETHODIMP
nsJAR::Extract(const char *zipEntry, nsIFile* outFile)
{

View File

@ -133,6 +133,30 @@ PR_PUBLIC_API(PRInt32) ZIP_OpenArchive( const char * zipname, void** hZip )
/**
* ZIP_TestArchive
*
* Tests the integrity of this open zip archive by extracting each
* item to memory and performing a CRC check.
*
* @param hZip handle obtained from ZIP_OpenArchive
* @return status code (success indicated by ZIP_OK)
*/
PR_PUBLIC_API(PRInt32) ZIP_TestArchive( void *hZip )
{
/*--- error check args ---*/
if ( hZip == 0 )
return ZIP_ERR_PARAM;
nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
if ( zip->kMagic != ZIP_MAGIC )
return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
/*--- test the archive ---*/
return zip->Test(NULL);
}
/**
* ZIP_CloseArchive
*
@ -320,6 +344,44 @@ PRInt32 nsZipArchive::OpenArchiveWithFileDesc(PRFileDesc* fd)
return BuildFileList();
}
//---------------------------------------------
// nsZipArchive::Test
//---------------------------------------------
PRInt32 nsZipArchive::Test(const char *aEntryName)
{
PRInt32 rv = ZIP_OK;
nsZipItem *currItem = 0;
if (aEntryName) // only test specified item
{
currItem = GetFileItem(aEntryName);
if(!currItem)
return ZIP_ERR_FNF;
rv = TestItem(currItem);
}
else // test all items in archive
{
nsZipFind *iterator = FindInit( NULL );
if (!iterator)
return ZIP_ERR_GENERAL;
// iterate over items in list
while (ZIP_OK == FindNext(iterator, &currItem))
{
rv = TestItem(currItem);
// check if crc check failed
if (rv != ZIP_OK)
break;
}
FindFree(iterator);
}
return rv;
}
//---------------------------------------------
// nsZipArchive::CloseArchive
//---------------------------------------------
@ -355,7 +417,6 @@ PRInt32 nsZipArchive::GetItem( const char * aFilename, nsZipItem **result)
if (aFilename == 0)
return ZIP_ERR_PARAM;
//PRInt32 result;
nsZipItem* item;
//-- find file information
@ -365,8 +426,7 @@ PRInt32 nsZipArchive::GetItem( const char * aFilename, nsZipItem **result)
return ZIP_ERR_FNF;
}
*result = item; // Return a pointer to the struct
*result = item; // Return a pointer to the struct
return ZIP_OK;
}
@ -800,7 +860,8 @@ PRInt32 nsZipArchive::BuildFileList()
//-- NOTE: extralen is different in central header and local header
//-- for archives created using the Unix "zip" utility. To set
//-- the offset accurately we need the local extralen.
if ( PR_Read( mFd, (char*)&Local, sizeof(ZipLocal) ) != (READTYPE)sizeof(ZipLocal) )
if ( PR_Read( mFd, (char*)&Local, sizeof(ZipLocal) ) !=
(READTYPE)sizeof(ZipLocal) )
{
//-- expected a complete local header
status = ZIP_ERR_CORRUPT;
@ -1182,6 +1243,184 @@ cleanup:
return status;
}
//---------------------------------------------
// nsZipArchive::TestItem
//---------------------------------------------
PRInt32 nsZipArchive::TestItem( const nsZipItem* aItem )
{
Bytef *inbuf = NULL, *outbuf = NULL, *old_next_out;
PRUint32 size, chunk, inpos, crc;
PRInt32 status = ZIP_OK;
int zerr = Z_OK;
z_stream zs;
PRBool bInflating = PR_FALSE;
PRBool bRead;
PRBool bWrote;
//-- param checks
if (!aItem)
return ZIP_ERR_PARAM;
if (aItem->compression != STORED && aItem->compression != DEFLATED)
return ZIP_ERR_UNSUPPORTED;
//-- allocate buffers
inbuf = (Bytef *) PR_Malloc(ZIP_BUFLEN);
if (aItem->compression == DEFLATED)
outbuf = (Bytef *) PR_Malloc(ZIP_BUFLEN);
if ( inbuf == 0 || (aItem->compression == DEFLATED && outbuf == 0) )
{
status = ZIP_ERR_MEMORY;
goto cleanup;
}
//-- seek to the item
#ifndef STANDALONE
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != (PRInt32)aItem->offset )
#else
if ( PR_Seek( mFd, aItem->offset, PR_SEEK_SET ) != 0)
#endif
{
status = ZIP_ERR_CORRUPT;
goto cleanup;
}
//-- set up the inflate if DEFLATED
if (aItem->compression == DEFLATED)
{
memset( &zs, 0, sizeof(zs) );
zerr = inflateInit2( &zs, -MAX_WBITS );
if ( zerr != Z_OK )
{
status = ZIP_ERR_GENERAL;
goto cleanup;
}
else
{
zs.next_out = outbuf;
zs.avail_out = ZIP_BUFLEN;
}
bInflating = PR_TRUE;
}
//-- initialize crc checksum
crc = crc32(0L, Z_NULL, 0);
size = aItem->size;
inpos = 0;
//-- read in ZIP_BUFLEN-sized chunks of item
//-- inflating if item is DEFLATED
while ( zerr == Z_OK )
{
bRead = PR_FALSE; // used to check if new data to inflate
bWrote = PR_FALSE; // used to reset zs.next_out to outbuf
// when outbuf fills up
//-- read to inbuf
if (aItem->compression == DEFLATED)
{
if ( zs.avail_in == 0 && zs.total_in < size )
{
//-- no data to inflate yet still more in file:
//-- read another chunk of compressed data
inpos = zs.total_in; // input position
chunk = ( inpos + ZIP_BUFLEN <= size ) ? ZIP_BUFLEN : size - inpos;
if ( PR_Read( mFd, inbuf, chunk ) != (READTYPE)chunk )
{
//-- unexpected end of data
status = ZIP_ERR_CORRUPT;
break;
}
zs.next_in = inbuf;
zs.avail_in = chunk;
bRead = PR_TRUE;
}
if ( zs.avail_out == 0 )
{
//-- reuse output buffer
zs.next_out = outbuf;
zs.avail_out = ZIP_BUFLEN;
bWrote = PR_TRUE; // mimic writing to disk/memory
}
}
else
{
if (inpos < size)
{
//-- read a chunk in
chunk = ( inpos + ZIP_BUFLEN <= size ) ? ZIP_BUFLEN : size - inpos;
if ( PR_Read( mFd, inbuf, chunk ) != (READTYPE)chunk )
{
//-- unexpected end of data
status = ZIP_ERR_CORRUPT;
break;
}
inpos += chunk;
}
else
{
//-- finished reading STORED item
break;
}
}
//-- inflate if item is DEFLATED
if (aItem->compression == DEFLATED)
{
if (bRead || bWrote)
{
old_next_out = zs.next_out;
zerr = inflate( &zs, Z_PARTIAL_FLUSH );
//-- incrementally update crc checksum
crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
}
else
zerr = Z_STREAM_END;
}
//-- else just use input buffer containing data from STORED item
else
{
//-- incrementally update crc checksum
crc = crc32(crc, (const unsigned char*)inbuf, chunk);
}
}
//-- convert zlib error to return value
if ( status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END )
{
status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
goto cleanup;
}
//-- verify computed crc checksum against header info crc
if (status == ZIP_OK && crc != aItem->crc32)
{
status = ZIP_ERR_CORRUPT;
}
cleanup:
if ( bInflating )
{
//-- free zlib internal state
inflateEnd( &zs );
}
PR_FREEIF(inbuf);
if (aItem->compression == DEFLATED)
PR_FREEIF(outbuf);
return status;
}
//------------------------------------------
// nsZipArchive constructor and destructor
//------------------------------------------

View File

@ -117,6 +117,17 @@ public:
PRInt32 OpenArchiveWithFileDesc(PRFileDesc* fd);
/**
* Test the integrity of items in this archive by running
* a CRC check after extracting each item into a memory
* buffer. If an entry name is supplied only the
* specified item is tested. Else, if null is supplied
* then all the items in the archive are tested.
*
* @return status code
*/
PRInt32 Test(const char *aEntryName);
/**
* Closes an open archive.
*/
@ -222,6 +233,7 @@ private:
PRInt32 InflateItem( const nsZipItem* aItem,
PRFileDesc* outFD,
char* buf );
PRInt32 TestItem( const nsZipItem *aItem );
};
/**

View File

@ -57,6 +57,12 @@ PR_EXTERN(PRInt32) ZIP_OpenArchive( const char * zipname, void** hZip );
PR_EXTERN(PRInt32) ZIP_CloseArchive( void** hZip );
/* Test the integrity of every item in this open archive
* by verifying each item's checksum against the stored
* CRC32 value.
*/
PR_EXTERN(PRInt32) ZIP_TestArchive( void* hZip );
/* Extract the named file in the archive to disk.
* This function will happily overwrite an existing Outfile if it can.
* It's up to the caller to detect or move it out of the way if it's important.