Bug 510844 - "Remove memcpy()s for compressed jar reading" [r=tglek]

This commit is contained in:
Alfred Kayser 2009-09-01 14:47:00 -05:00
parent c61ee4488f
commit f37c1ed9ed
5 changed files with 173 additions and 377 deletions

View File

@ -339,7 +339,7 @@ nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
if (!item || item->isDirectory) { if (!item || item->isDirectory) {
rv = jis->InitDirectory(this, aJarDirSpec, aEntryName); rv = jis->InitDirectory(this, aJarDirSpec, aEntryName);
} else { } else {
rv = jis->InitFile(mZip.GetFD(item), item); rv = jis->InitFile(this, item);
} }
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_RELEASE(*result); NS_RELEASE(*result);

View File

@ -59,43 +59,42 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream)
*--------------------------------------------------------*/ *--------------------------------------------------------*/
nsresult nsresult
nsJARInputStream::InitFile(nsZipHandle *aFd, nsZipItem *item) nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item)
{ {
nsresult rv; nsresult rv = NS_OK;
NS_ABORT_IF_FALSE(aFd, "Argument may not be null"); NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
NS_ABORT_IF_FALSE(item, "Argument may not be null"); NS_ABORT_IF_FALSE(item, "Argument may not be null");
// Mark it as closed, in case something fails in initialisation // Mark it as closed, in case something fails in initialisation
mClosed = PR_TRUE; mClosed = true;
// Keep the important bits of nsZipItem only
mInSize = item->size;
//-- prepare for the compression type //-- prepare for the compression type
switch (item->compression) { switch (item->compression) {
case STORED: case STORED:
mCompressed = false;
break; break;
case DEFLATED: case DEFLATED:
mInflate = (InflateStruct *) PR_Malloc(sizeof(InflateStruct)); mCompressed = true;
NS_ENSURE_TRUE(mInflate, NS_ERROR_OUT_OF_MEMORY); rv = gZlibInit(&mZs);
rv = gZlibInit(&(mInflate->mZs));
NS_ENSURE_SUCCESS(rv, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_SUCCESS(rv, NS_ERROR_OUT_OF_MEMORY);
mInflate->mOutSize = item->realsize; mOutSize = item->realsize;
mInflate->mInCrc = item->crc32; mInCrc = item->crc32;
mInflate->mOutCrc = crc32(0L, Z_NULL, 0); mOutCrc = crc32(0L, Z_NULL, 0);
break; break;
default: default:
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
//-- Set filepointer to start of item // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
mFd.Open(aFd, item->dataOffset, item->size); mFd = aJar->mZip.GetFD();
mZs.next_in = aJar->mZip.GetData(item);
mZs.avail_in = item->size;
mOutSize = item->realsize;
mDirectory = false;
// Open for reading // Open for reading
mClosed = PR_FALSE; mClosed = false;
mCurPos = 0; mCurPos = 0;
return NS_OK; return NS_OK;
} }
@ -109,8 +108,9 @@ nsJARInputStream::InitDirectory(nsJAR* aJar,
NS_ABORT_IF_FALSE(aDir, "Argument may not be null"); NS_ABORT_IF_FALSE(aDir, "Argument may not be null");
// Mark it as closed, in case something fails in initialisation // Mark it as closed, in case something fails in initialisation
mClosed = PR_TRUE; mClosed = true;
mDirectory = PR_TRUE; mDirectory = true;
mCompressed = false;
// Keep the zipReader for getting the actual zipItems // Keep the zipReader for getting the actual zipItems
mJar = aJar; mJar = aJar;
@ -173,7 +173,7 @@ nsJARInputStream::InitDirectory(nsJAR* aJar,
mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n"); mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n");
// Open for reading // Open for reading
mClosed = PR_FALSE; mClosed = false;
mCurPos = 0; mCurPos = 0;
mArrPos = 0; mArrPos = 0;
return NS_OK; return NS_OK;
@ -187,10 +187,10 @@ nsJARInputStream::Available(PRUint32 *_retval)
if (mDirectory) if (mDirectory)
*_retval = mBuffer.Length(); *_retval = mBuffer.Length();
else if (mInflate) else if (mCompressed)
*_retval = mInflate->mOutSize - mInflate->mZs.total_out; *_retval = mOutSize - mZs.total_out;
else else
*_retval = mInSize - mCurPos; *_retval = mOutSize - mCurPos;
return NS_OK; return NS_OK;
} }
@ -202,41 +202,30 @@ nsJARInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
*aBytesRead = 0; *aBytesRead = 0;
nsresult rv = NS_OK;
if (mClosed) if (mClosed)
return rv; return NS_OK;
if (mDirectory) { if (mDirectory) {
rv = ReadDirectory(aBuffer, aCount, aBytesRead); return ReadDirectory(aBuffer, aCount, aBytesRead);
} else { }
if (mInflate) { if (mCompressed) {
rv = ContinueInflate(aBuffer, aCount, aBytesRead); return ContinueInflate(aBuffer, aCount, aBytesRead);
} else { }
PRInt32 bytesRead = 0; if (mFd) {
aCount = PR_MIN(aCount, mInSize - mCurPos); PRUint32 count = PR_MIN(aCount, mOutSize - mCurPos);
if (aCount) { memcpy(aBuffer, mZs.next_in + mCurPos, count);
bytesRead = mFd.Read(aBuffer, aCount); mCurPos += count;
if (bytesRead < 0) *aBytesRead = count;
return NS_ERROR_FILE_CORRUPTED;
mCurPos += bytesRead;
if ((PRUint32)bytesRead != aCount) {
// file is truncated or was lying about size, we're done
Close();
return NS_ERROR_FILE_CORRUPTED;
}
}
*aBytesRead = bytesRead;
}
// be aggressive about closing! // be aggressive about closing!
// note that sometimes, we will close mFd before we've finished // note that sometimes, we will close mFd before we've finished
// deflating - this is because zlib buffers the input // deflating - this is because zlib buffers the input
// So, don't free the ReadBuf/InflateStruct yet. // So, don't free the ReadBuf/InflateStruct yet.
// It is ok to close the fd multiple times (also in nsJARInputStream::Close()) // It is ok to close the fd multiple times (also in nsJARInputStream::Close())
if (mCurPos >= mInSize) { if (mCurPos >= mZs.avail_in) {
mFd.Close(); mFd = nsnull;
} }
} }
return rv; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -257,9 +246,9 @@ nsJARInputStream::IsNonBlocking(PRBool *aNonBlocking)
NS_IMETHODIMP NS_IMETHODIMP
nsJARInputStream::Close() nsJARInputStream::Close()
{ {
PR_FREEIF(mInflate); mClosed = true;
mClosed = PR_TRUE; mFd = nsnull;
mFd.Close(); mJar = nsnull;
return NS_OK; return NS_OK;
} }
@ -268,56 +257,33 @@ nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount,
PRUint32* aBytesRead) PRUint32* aBytesRead)
{ {
// No need to check the args, ::Read did that, but assert them at least // No need to check the args, ::Read did that, but assert them at least
NS_ASSERTION(mInflate,"inflate data structure missing");
NS_ASSERTION(aBuffer,"aBuffer parameter must not be null"); NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null"); NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
// Keep old total_out count // Keep old total_out count
const PRUint32 oldTotalOut = mInflate->mZs.total_out; const PRUint32 oldTotalOut = mZs.total_out;
// make sure we aren't reading too much // make sure we aren't reading too much
mInflate->mZs.avail_out = (mInflate->mOutSize-oldTotalOut > aCount) ? aCount : mInflate->mOutSize-oldTotalOut; mZs.avail_out = PR_MIN(aCount, (mOutSize-oldTotalOut));
mInflate->mZs.next_out = (unsigned char*)aBuffer; mZs.next_out = (unsigned char*)aBuffer;
int zerr = Z_OK;
//-- inflate loop
while (mInflate->mZs.avail_out > 0 && zerr == Z_OK) {
if (mInflate->mZs.avail_in == 0 && mCurPos < mInSize) {
// time to fill the buffer!
PRUint32 bytesToRead = PR_MIN(mInSize - mCurPos, ZIP_BUFLEN);
PRInt32 bytesRead = mFd.Read(mInflate->mReadBuf, bytesToRead);
if (bytesRead < 0) {
zerr = Z_ERRNO;
break;
}
mCurPos += bytesRead;
// now set the state for 'inflate'
mInflate->mZs.next_in = mInflate->mReadBuf;
mInflate->mZs.avail_in = bytesRead;
}
// now inflate
zerr = inflate(&(mInflate->mZs), Z_SYNC_FLUSH);
}
// now inflate
int zerr = inflate(&mZs, Z_SYNC_FLUSH);
if ((zerr != Z_OK) && (zerr != Z_STREAM_END)) if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
return NS_ERROR_FILE_CORRUPTED; return NS_ERROR_FILE_CORRUPTED;
*aBytesRead = (mInflate->mZs.total_out - oldTotalOut); *aBytesRead = (mZs.total_out - oldTotalOut);
// Calculate the CRC on the output // Calculate the CRC on the output
mInflate->mOutCrc = crc32(mInflate->mOutCrc, (unsigned char*)aBuffer, *aBytesRead); mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
// be aggressive about ending the inflation // be aggressive about ending the inflation
// for some reason we don't always get Z_STREAM_END // for some reason we don't always get Z_STREAM_END
if (zerr == Z_STREAM_END || mInflate->mZs.total_out == mInflate->mOutSize) { if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
inflateEnd(&(mInflate->mZs)); inflateEnd(&mZs);
// stop returning valid data as soon as we know we have a bad CRC // stop returning valid data as soon as we know we have a bad CRC
if (mInflate->mOutCrc != mInflate->mInCrc) { if (mOutCrc != mInCrc) {
// asserting because while this rarely happens, you definitely // asserting because while this rarely happens, you definitely
// want to catch it in debug builds! // want to catch it in debug builds!
NS_NOTREACHED(0); NS_NOTREACHED(0);

View File

@ -53,45 +53,39 @@ class nsJARInputStream : public nsIInputStream
{ {
public: public:
nsJARInputStream() : nsJARInputStream() :
mInSize(0), mCurPos(0), mInflate(nsnull), mDirectory(0), mClosed(PR_FALSE) mCurPos(0), mCompressed(false), mDirectory(false), mClosed(true)
{ } { }
~nsJARInputStream() { ~nsJARInputStream() { Close(); }
Close();
}
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSIINPUTSTREAM NS_DECL_NSIINPUTSTREAM
// takes ownership of |fd|, even on failure // takes ownership of |fd|, even on failure
nsresult InitFile(nsZipHandle *aFd, nsZipItem *item); nsresult InitFile(nsJAR *aJar, nsZipItem *item);
nsresult InitDirectory(nsJAR *aJar, nsresult InitDirectory(nsJAR *aJar,
const nsACString& aJarDirSpec, const nsACString& aJarDirSpec,
const char* aDir); const char* aDir);
private: private:
PRUint32 mInSize; // Size in original file nsRefPtr<nsZipHandle> mFd; // handle for reading
PRUint32 mCurPos; // Current position in input PRUint32 mCurPos; // Current position in input
PRUint32 mOutSize; // inflated size
struct InflateStruct { PRUint32 mInCrc; // CRC as provided by the zipentry
PRUint32 mOutSize; // inflated size PRUint32 mOutCrc; // CRC as calculated by me
PRUint32 mInCrc; // CRC as provided by the zipentry z_stream mZs; // zip data structure
PRUint32 mOutCrc; // CRC as calculated by me
z_stream mZs; // zip data structure
unsigned char mReadBuf[ZIP_BUFLEN]; // Readbuffer to inflate from
};
struct InflateStruct * mInflate;
/* For directory reading */ /* For directory reading */
nsRefPtr<nsJAR> mJar; // string reference to zipreader nsRefPtr<nsJAR> mJar; // string reference to zipreader
PRUint32 mNameLen; // length of dirname PRUint32 mNameLen; // length of dirname
nsCAutoString mBuffer; // storage for generated text of stream nsCString mBuffer; // storage for generated text of stream
PRUint32 mArrPos; // current position within mArray PRUint32 mArrPos; // current position within mArray
nsTArray<nsCString> mArray; // array of names in (zip) directory nsTArray<nsCString> mArray; // array of names in (zip) directory
PRPackedBool mDirectory; // is this a directory?
PRPackedBool mClosed; // Whether the stream is closed bool mCompressed; // is this compressed?
nsSeekableZipHandle mFd; // handle for reading bool mDirectory; // is this a directory?
bool mClosed; // Whether the stream is closed
nsresult ContinueInflate(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead); nsresult ContinueInflate(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);
nsresult ReadDirectory(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead); nsresult ReadDirectory(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);

View File

@ -50,7 +50,6 @@
*/ */
#define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
#define READTYPE PRInt32 #define READTYPE PRInt32
#include "zlib.h" #include "zlib.h"
#include "nsISupportsUtils.h" #include "nsISupportsUtils.h"
@ -173,7 +172,7 @@ zlibAlloc(void *opaque, uInt items, uInt size)
PRUint32 realitems = items; PRUint32 realitems = items;
if (size == 4 && items < BY4ALLOC_ITEMS) if (size == 4 && items < BY4ALLOC_ITEMS)
realitems = BY4ALLOC_ITEMS; realitems = BY4ALLOC_ITEMS;
return zallocator->Calloc(realitems, size); return zallocator->Calloc(realitems, size);
} }
else else
return calloc(items, size); return calloc(items, size);
@ -228,13 +227,14 @@ nsresult nsZipHandle::Init(PRFileDesc *fd, nsZipHandle **ret)
return NS_ERROR_FILE_TOO_BIG; return NS_ERROR_FILE_TOO_BIG;
PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY); PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
if (!map) if (!map)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
nsZipHandle *handle = new nsZipHandle(); nsZipHandle *handle = new nsZipHandle();
if (!handle) if (!handle) {
PR_CloseFileMap(map);
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
}
handle->mFd = fd; handle->mFd = fd;
handle->mMap = map; handle->mMap = map;
@ -245,22 +245,17 @@ nsresult nsZipHandle::Init(PRFileDesc *fd, nsZipHandle **ret)
return NS_OK; return NS_OK;
} }
PRInt32 nsZipHandle::Read(PRUint32 aPosition, void *aBuffer, PRUint32 aCount)
{
if (mLen < 0 || aPosition+aCount > (PRUint32) mLen)
return -1;
PRInt32 count = PR_MIN(aCount, mLen - aPosition);
memcpy(aBuffer, mFileData + aPosition, count);
return count;
}
nsZipHandle::~nsZipHandle() nsZipHandle::~nsZipHandle()
{ {
if (mFd) { if (mFileData) {
PR_MemUnmap(mFileData, mLen); PR_MemUnmap(mFileData, mLen);
PR_CloseFileMap(mMap); PR_CloseFileMap(mMap);
mFileData = nsnull;
mMap = nsnull;
}
if (mFd) {
PR_Close(mFd); PR_Close(mFd);
mFd = 0; mFd = nsnull;
} }
MOZ_COUNT_DTOR(nsZipHandle); MOZ_COUNT_DTOR(nsZipHandle);
} }
@ -326,6 +321,7 @@ nsresult nsZipArchive::CloseArchive()
{ {
if (mFd) { if (mFd) {
PL_FinishArenaPool(&mArena); PL_FinishArenaPool(&mArena);
mFd = NULL;
} }
// CAUTION: // CAUTION:
@ -335,11 +331,8 @@ nsresult nsZipArchive::CloseArchive()
// for all the nsZipItem in one shot. But if the ~nsZipItem is doing // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
// anything more than cleaning up memory, we should start calling it. // anything more than cleaning up memory, we should start calling it.
// Let us also cleanup the mFiles table for re-use on the next 'open' call // Let us also cleanup the mFiles table for re-use on the next 'open' call
for (int i = 0; i < ZIP_TABSIZE; i++) { memset(mFiles, 0, sizeof(mFiles));
mFiles[i] = 0; mBuiltSynthetics = false;
}
mFd = NULL;
mBuiltSynthetics = PR_FALSE;
return ZIP_OK; return ZIP_OK;
} }
@ -388,25 +381,17 @@ nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
// so the item to be extracted should never be a directory // so the item to be extracted should never be a directory
PR_ASSERT(!item->isDirectory); PR_ASSERT(!item->isDirectory);
//-- move to the start of file's data
if (!MaybeReadItem(item))
return ZIP_ERR_CORRUPT;
nsSeekableZipHandle fd;
if (!fd.Open(mFd.get(), item->dataOffset, item->size))
return ZIP_ERR_CORRUPT;
nsresult rv; nsresult rv;
//-- extract the file using the appropriate method //-- extract the file using the appropriate method
switch(item->compression) switch(item->compression)
{ {
case STORED: case STORED:
rv = CopyItemToDisk(item->size, item->crc32, fd, aFd); rv = CopyItemToDisk(item, aFd);
break; break;
case DEFLATED: case DEFLATED:
rv = InflateItem(item, fd, aFd); rv = InflateItem(item, aFd);
break; break;
default: default:
@ -607,14 +592,12 @@ nsresult nsZipArchive::BuildFileList()
return ZIP_ERR_MEMORY; return ZIP_ERR_MEMORY;
item->headerOffset = xtolong(central->localhdr_offset); item->headerOffset = xtolong(central->localhdr_offset);
item->dataOffset = 0;
item->size = xtolong(central->size); item->size = xtolong(central->size);
item->realsize = xtolong(central->orglen); item->realsize = xtolong(central->orglen);
item->crc32 = xtolong(central->crc32); item->crc32 = xtolong(central->crc32);
item->time = xtoint(central->time); item->time = xtoint(central->time);
item->date = xtoint(central->date); item->date = xtoint(central->date);
item->isSynthetic = PR_FALSE; item->isSynthetic = PR_FALSE;
item->hasDataOffset = PR_FALSE;
item->compression = PR_MIN(xtoint(central->method), UNSUPPORTED); item->compression = PR_MIN(xtoint(central->method), UNSUPPORTED);
item->mode = ExtractMode(central->external_attributes); item->mode = ExtractMode(central->external_attributes);
#if defined(XP_UNIX) || defined(XP_BEOS) #if defined(XP_UNIX) || defined(XP_BEOS)
@ -652,7 +635,7 @@ nsresult nsZipArchive::BuildSynthetics()
{ {
if (mBuiltSynthetics) if (mBuiltSynthetics)
return ZIP_OK; return ZIP_OK;
mBuiltSynthetics = PR_TRUE; mBuiltSynthetics = true;
// Create synthetic entries for any missing directories. // Create synthetic entries for any missing directories.
// Do this when all ziptable has scanned to prevent double entries. // Do this when all ziptable has scanned to prevent double entries.
@ -737,91 +720,68 @@ nsresult nsZipArchive::BuildSynthetics()
return ZIP_OK; return ZIP_OK;
} }
nsZipHandle* nsZipArchive::GetFD(nsZipItem* aItem) nsZipHandle* nsZipArchive::GetFD()
{ {
if (!mFd || !MaybeReadItem(aItem)) if (!mFd)
return NULL; return NULL;
return mFd.get(); return mFd.get();
} }
//--------------------------------------------- //---------------------------------------------
// nsZipArchive::MaybeReadItem // nsZipArchive::GetData
//--------------------------------------------- //---------------------------------------------
bool nsZipArchive::MaybeReadItem(nsZipItem* aItem) PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
{ {
PR_ASSERT (aItem); PR_ASSERT (aItem);
//-- the first time an item is used we need to calculate its offset //-- read local header to get variable length values and calculate
if (!aItem->hasDataOffset) //-- the real data offset
{ if (aItem->headerOffset + ZIPLOCAL_SIZE > mFd->mLen)
//-- read local header to get variable length values and calculate return nsnull;
//-- the real data offset
//--
//-- 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 (!mFd || !mFd->mLen > aItem->headerOffset + ZIPLOCAL_SIZE)
return false;
ZipLocal *Local = (ZipLocal*)(mFd->mFileData + aItem->headerOffset); // -- check signature before using the structure, in case the zip file is corrupt
//check limits here ZipLocal* Local = (ZipLocal*)(mFd->mFileData + aItem->headerOffset);
if ((xtolong(Local->signature) != LOCALSIG)) if ((xtolong(Local->signature) != LOCALSIG))
{ return nsnull;
//-- read error or local header not found
return false;
}
aItem->dataOffset = aItem->headerOffset + //-- 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.
PRUint32 dataOffset = aItem->headerOffset +
ZIPLOCAL_SIZE + ZIPLOCAL_SIZE +
xtoint(Local->filename_len) + xtoint(Local->filename_len) +
xtoint(Local->extrafield_len); xtoint(Local->extrafield_len);
aItem->hasDataOffset = PR_TRUE;
}
return true; // -- check if there is enough source data in the file
if (dataOffset + aItem->size > mFd->mLen)
return nsnull;
return mFd->mFileData + dataOffset;
} }
//--------------------------------------------- //---------------------------------------------
// nsZipArchive::CopyItemToDisk // nsZipArchive::CopyItemToDisk
//--------------------------------------------- //---------------------------------------------
nsresult nsresult
nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, nsZipArchive::CopyItemToDisk(nsZipItem *item, PRFileDesc* outFD)
nsSeekableZipHandle &fd, PRFileDesc* outFD)
/*
* This function copies an archive item to disk, to the
* file specified by outFD. If outFD is zero, the extracted data is
* not written, only checked for CRC, so this is in effect same as 'Test'.
*/
{ {
PRUint32 chunk, pos, crc; PR_ASSERT(item);
char buf[ZIP_BUFLEN];
//-- initialize crc //-- get to the start of file's data
crc = crc32(0L, Z_NULL, 0); const PRUint8* itemData = GetData(item);
if (!itemData)
return ZIP_ERR_CORRUPT;
//-- copy chunks until file is done if (outFD && PR_Write(outFD, itemData, item->size) < (READTYPE)item->size)
for (pos = 0; pos < itemSize; pos += chunk)
{ {
chunk = (itemSize - pos < ZIP_BUFLEN) ? (itemSize - pos) : ZIP_BUFLEN; //-- Couldn't write all the data (disk full?)
return ZIP_ERR_DISK;
if (fd.Read(buf, chunk) != (READTYPE)chunk)
{
//-- unexpected end of data in archive
return ZIP_ERR_CORRUPT;
}
//-- incrementally update crc32
crc = crc32(crc, (const unsigned char*)buf, chunk);
if (outFD && PR_Write(outFD, buf, chunk) < (READTYPE)chunk)
{
//-- Couldn't write all the data (disk full?)
return ZIP_ERR_DISK;
}
} }
//-- Calculate crc
PRUint32 crc = crc32(0L, (const unsigned char*)itemData, item->size);
//-- verify crc32 //-- verify crc32
if (crc != itemCrc) if (crc != item->crc32)
return ZIP_ERR_CORRUPT; return ZIP_ERR_CORRUPT;
return ZIP_OK; return ZIP_OK;
@ -831,17 +791,15 @@ nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc,
//--------------------------------------------- //---------------------------------------------
// nsZipArchive::InflateItem // nsZipArchive::InflateItem
//--------------------------------------------- //---------------------------------------------
nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD) nsresult nsZipArchive::InflateItem(nsZipItem * item, PRFileDesc* outFD)
/* /*
* This function inflates an archive item to disk, to the * This function inflates an archive item to disk, to the
* file specified by outFD. If outFD is zero, the extracted data is * file specified by outFD. If outFD is zero, the extracted data is
* not written, only checked for CRC, so this is in effect same as 'Test'. * not written, only checked for CRC, so this is in effect same as 'Test'.
*/ */
{ {
PR_ASSERT(aItem); PR_ASSERT(item);
//-- allocate deflation buffers //-- allocate deflation buffers
Bytef inbuf[ZIP_BUFLEN];
Bytef outbuf[ZIP_BUFLEN]; Bytef outbuf[ZIP_BUFLEN];
//-- set up the inflate //-- set up the inflate
@ -851,95 +809,44 @@ nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &
return ZIP_ERR_GENERAL; return ZIP_ERR_GENERAL;
//-- inflate loop //-- inflate loop
zs.next_out = outbuf; zs.avail_in = item->size;
zs.avail_out = ZIP_BUFLEN; zs.next_in = (Bytef*)GetData(item);
if (!zs.next_in)
return ZIP_ERR_CORRUPT;
PRUint32 size = aItem->size;
PRUint32 outpos = 0;
PRUint32 crc = crc32(0L, Z_NULL, 0); PRUint32 crc = crc32(0L, Z_NULL, 0);
int zerr = Z_OK; int zerr = Z_OK;
while (zerr == Z_OK) while (zerr == Z_OK)
{ {
PRBool bRead = PR_FALSE; zs.next_out = outbuf;
PRBool bWrote= PR_FALSE; zs.avail_out = ZIP_BUFLEN;
if (zs.avail_in == 0 && zs.total_in < size) zerr = inflate(&zs, Z_PARTIAL_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END)
{ {
//-- no data to inflate yet still more in file: status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
//-- read another chunk of compressed data break;
PRUint32 chunk = (size-zs.total_in < ZIP_BUFLEN) ? size-zs.total_in : ZIP_BUFLEN;
if (fd.Read(inbuf, chunk) != (READTYPE)chunk)
{
//-- unexpected end of data
status = ZIP_ERR_CORRUPT;
break;
}
zs.next_in = inbuf;
zs.avail_in = chunk;
bRead = PR_TRUE;
} }
PRUint32 count = zs.next_out - outbuf;
if (zs.avail_out == 0) //-- incrementally update crc32
crc = crc32(crc, (const unsigned char*)outbuf, count);
if (outFD && PR_Write(outFD, outbuf, count) < (READTYPE)count)
{ {
//-- write inflated buffer to disk and make space status = ZIP_ERR_DISK;
if (outFD && PR_Write(outFD, outbuf, ZIP_BUFLEN) < ZIP_BUFLEN) break;
{
//-- Couldn't write all the data (disk full?)
status = ZIP_ERR_DISK;
break;
}
outpos = zs.total_out;
zs.next_out = outbuf;
zs.avail_out = ZIP_BUFLEN;
bWrote = PR_TRUE;
} }
if(bRead || bWrote)
{
Bytef* old_next_out = zs.next_out;
zerr = inflate(&zs, Z_PARTIAL_FLUSH);
//-- incrementally update crc32
crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
}
else
zerr = Z_STREAM_END;
} // while } // while
//-- verify crc32
if ((status == ZIP_OK) && (crc != aItem->crc32))
{
status = ZIP_ERR_CORRUPT;
goto cleanup;
}
//-- write last inflated bit to disk
if (zerr == Z_STREAM_END && outpos < zs.total_out)
{
PRUint32 chunk = zs.total_out - outpos;
if (outFD && PR_Write(outFD, outbuf, chunk) < (READTYPE)chunk)
status = ZIP_ERR_DISK;
}
//-- 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;
}
//-- if found no errors make sure we've converted the whole thing
PR_ASSERT(status != ZIP_OK || zs.total_in == aItem->size);
PR_ASSERT(status != ZIP_OK || zs.total_out == aItem->realsize);
cleanup:
//-- free zlib internal state //-- free zlib internal state
inflateEnd(&zs); inflateEnd(&zs);
//-- verify crc32
if ((status == ZIP_OK) && (crc != item->crc32))
{
status = ZIP_ERR_CORRUPT;
}
return status; return status;
} }
@ -948,12 +855,12 @@ cleanup:
//------------------------------------------ //------------------------------------------
nsZipArchive::nsZipArchive() : nsZipArchive::nsZipArchive() :
mBuiltSynthetics(PR_FALSE) mBuiltSynthetics(false)
{ {
MOZ_COUNT_CTOR(nsZipArchive); MOZ_COUNT_CTOR(nsZipArchive);
// initialize the table to NULL // initialize the table to NULL
memset(mFiles, 0, sizeof mFiles); memset(mFiles, 0, sizeof(mFiles));
} }
nsZipArchive::~nsZipArchive() nsZipArchive::~nsZipArchive()

View File

@ -46,19 +46,16 @@
#define ZIP_MAGIC 0x5A49505FL /* "ZIP_" */ #define ZIP_MAGIC 0x5A49505FL /* "ZIP_" */
#define ZIPFIND_MAGIC 0x5A495046L /* "ZIPF" */ #define ZIPFIND_MAGIC 0x5A495046L /* "ZIPF" */
#define ZIP_TABSIZE 256 #define ZIP_TABSIZE 256
// Keep this odd. The -1 is significant. /* We really want to be a (multiple of) 4K for optimal file IO */
#define ZIP_BUFLEN (4 * 1024 - 1) #define ZIP_BUFLEN (4*1024) /* Used as output buffer when deflating items to a file */
#define PL_ARENA_CONST_ALIGN_MASK 7 #define PL_ARENA_CONST_ALIGN_MASK 7
#include "plarena.h" #include "plarena.h"
#define ZIP_Seek(fd,p,m) (PR_Seek((fd),((PROffset32)p),(m))==((PROffset32)p))
#include "zlib.h" #include "zlib.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
class nsZipFind; class nsZipFind;
class nsZipReadState;
class nsZipItemMetadata;
struct PRFileDesc; struct PRFileDesc;
@ -88,7 +85,6 @@ struct nsZipItem
nsZipItem* next; nsZipItem* next;
PRUint32 headerOffset; PRUint32 headerOffset;
PRUint32 dataOffset;
PRUint32 size; /* size in original file */ PRUint32 size; /* size in original file */
PRUint32 realsize; /* inflated size */ PRUint32 realsize; /* inflated size */
PRUint32 crc32; PRUint32 crc32;
@ -100,21 +96,18 @@ struct nsZipItem
PRUint16 date; PRUint16 date;
PRUint16 mode; PRUint16 mode;
PRUint8 compression; PRUint8 compression;
PRPackedBool hasDataOffset : 1; bool isDirectory;
PRPackedBool isDirectory : 1; bool isSynthetic; /* whether item is an actual zip entry or was
PRPackedBool isSynthetic : 1; /* whether item is an actual zip entry or was generated as part of a real entry's path */
generated as part of a real entry's path,
e.g. foo/ in a zip containing only foo/a.txt
and no foo/ entry is synthetic */
#if defined(XP_UNIX) || defined(XP_BEOS) #if defined(XP_UNIX) || defined(XP_BEOS)
PRPackedBool isSymlink : 1; bool isSymlink;
#endif #endif
char name[1]; // actually, bigger than 1 char name[1]; /* actually, bigger than 1 */
}; };
class nsZipHandle; class nsZipHandle;
class nsSeekableZipHandle;
/** /**
* nsZipArchive -- a class for reading the PKZIP file format. * nsZipArchive -- a class for reading the PKZIP file format.
* *
@ -190,10 +183,17 @@ public:
*/ */
PRInt32 FindInit(const char * aPattern, nsZipFind** aFind); PRInt32 FindInit(const char * aPattern, nsZipFind** aFind);
/* Gets an undependent handle to the jar /*
* Also ensures that aItem is fully filled * Gets an undependent handle to the mapped file.
*/ */
nsZipHandle* GetFD(nsZipItem* aItem); nsZipHandle* GetFD();
/**
* Get pointer to the data of the item.
* @param aItem Pointer to nsZipItem
* reutrns null when zip file is corrupt.
*/
PRUint8* GetData(nsZipItem* aItem);
private: private:
//--- private members --- //--- private members ---
@ -201,15 +201,8 @@ private:
nsZipItem* mFiles[ZIP_TABSIZE]; nsZipItem* mFiles[ZIP_TABSIZE];
PLArenaPool mArena; PLArenaPool mArena;
/**
* Fills in nsZipItem fields that were not filled in by BuildFileList
* @param aItem Pointer to nsZipItem
* returns true if the item was filled in successfully
*/
bool MaybeReadItem(nsZipItem* aItem);
// Whether we synthesized the directory entries // Whether we synthesized the directory entries
PRPackedBool mBuiltSynthetics; bool mBuiltSynthetics;
// file handle // file handle
nsRefPtr<nsZipHandle> mFd; nsRefPtr<nsZipHandle> mFd;
@ -222,92 +215,29 @@ private:
nsresult BuildFileList(); nsresult BuildFileList();
nsresult BuildSynthetics(); nsresult BuildSynthetics();
nsresult CopyItemToDisk(PRUint32 size, PRUint32 crc, nsSeekableZipHandle &fd, PRFileDesc* outFD); nsresult CopyItemToDisk(nsZipItem* item, PRFileDesc* outFD);
nsresult InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD); nsresult InflateItem(nsZipItem* item, PRFileDesc* outFD);
}; };
class nsZipHandle { class nsZipHandle {
friend class nsZipArchive; friend class nsZipArchive;
friend class nsSeekableZipHandle;
public: public:
static nsresult Init(PRFileDesc *fd, nsZipHandle **ret NS_OUTPARAM); static nsresult Init(PRFileDesc *fd, nsZipHandle **ret NS_OUTPARAM);
/**
* Reads data at a certain point
* @param aPosition seek ofset
* @param aBuffer buffer
* @param aCount number of bytes to read */
PRInt32 Read(PRUint32 aPosition, void *aBuffer, PRUint32 aCount);
NS_METHOD_(nsrefcnt) AddRef(void); NS_METHOD_(nsrefcnt) AddRef(void);
NS_METHOD_(nsrefcnt) Release(void); NS_METHOD_(nsrefcnt) Release(void);
protected: protected:
PRFileDesc *mFd; // OS file-descriptor PRFileDesc * mFd; /* OS file-descriptor */
PRUint8 *mFileData; // pointer to mmaped file PRUint8 * mFileData; /* pointer to mmaped file */
PRUint32 mLen; // length of file and memory mapped area PRUint32 mLen; /* length of file and memory mapped area */
private: private:
nsZipHandle(); nsZipHandle();
~nsZipHandle(); ~nsZipHandle();
PRFileMap *mMap; // nspr datastructure for mmap PRFileMap * mMap; /* nspr datastructure for mmap */
nsrefcnt mRefCnt; // ref count nsrefcnt mRefCnt; /* ref count */
};
/** nsSeekableZipHandle acts as a container for nsZipHandle,
emulates sequential file io */
class nsSeekableZipHandle {
// stick nsZipItem in here
public:
nsSeekableZipHandle()
: mOffset(0)
, mRemaining(0)
{
}
/** Initializes nsSeekableZipHandle with
* @param aOffset byte offset of the file to start reading at
* @param length of this descriptor
*/
bool Open(nsZipHandle *aHandle, PRUint32 aOffset, PRUint32 aLength) {
NS_ABORT_IF_FALSE (aHandle, "Argument must not be NULL");
if (aOffset > aHandle->mLen)
return false;
mFd = aHandle;
mOffset = aOffset;
mRemaining = aLength;
return true;
}
/** Releases the file handle. It is safe to call multiple times. */
void Close()
{
mFd = NULL;
}
/**
* Reads data at a certain point
* @param aBuffer input buffer
* @param aCount number of bytes to read */
PRInt32 Read(void *aBuffer, PRUint32 aCount)
{
if (!mFd.get())
return -1;
aCount = PR_MIN(mRemaining, aCount);
PRInt32 ret = mFd->Read(mOffset, aBuffer, aCount);
if (ret > 0) {
mOffset += ret;
mRemaining -= ret;
}
return ret;
}
private:
nsRefPtr<nsZipHandle> mFd; // file handle
PRUint32 mOffset; // current reading offset
PRUint32 mRemaining; // bytes remaining
}; };
@ -319,7 +249,6 @@ private:
class nsZipFind class nsZipFind
{ {
public: public:
nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool regExp); nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool regExp);
~nsZipFind(); ~nsZipFind();