--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -117,17 +117,16 @@ DeleteManifestEntry(nsHashKey* aKey, voi
// The following initialization makes a guess of 10 entries per jarfile.
nsJAR::nsJAR(): mManifestData(nsnull, nsnull, DeleteManifestEntry, nsnull, 10),
mParsedManifest(PR_FALSE),
mGlobalStatus(JAR_MANIFEST_NOT_PARSED),
mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
mCache(nsnull),
mLock(nsnull),
- mMtime(0),
mTotalItemsInManifest(0)
{
}
nsJAR::~nsJAR()
{
Close();
}
@@ -162,26 +161,24 @@ nsrefcnt nsJAR::Release(void)
NS_IMETHODIMP
nsJAR::Open(nsIFile* zipFile)
{
NS_ENSURE_ARG_POINTER(zipFile);
if (mLock) return NS_ERROR_FAILURE; // Already open!
mZipFile = zipFile;
- nsresult rv = zipFile->GetLastModifiedTime(&mMtime);
- if (NS_FAILED(rv)) return rv;
mLock = PR_NewLock();
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
PRFileDesc *fd = OpenFile();
NS_ENSURE_TRUE(fd, NS_ERROR_FAILURE);
- rv = mZip.OpenArchive(fd);
+ nsresult rv = mZip.OpenArchive(fd);
if (NS_FAILED(rv)) Close();
return rv;
}
NS_IMETHODIMP
nsJAR::GetFile(nsIFile* *result)
{
@@ -335,29 +332,19 @@ nsJAR::GetInputStreamWithSpec(const nsAC
}
nsJARInputStream* jis = new nsJARInputStream();
// addref now so we can call InitFile/InitDirectory()
NS_ENSURE_TRUE(jis, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*result = jis);
nsresult rv = NS_OK;
if (!item || item->isDirectory) {
- rv = jis->InitDirectory(&mZip, aJarDirSpec, aEntryName);
+ rv = jis->InitDirectory(this, aJarDirSpec, aEntryName);
} else {
- // Open jarfile, to get its own filedescriptor for the stream
- // XXX The file may have been overwritten, so |item| might not be
- // valid. We really want to work from inode rather than file name.
- PRFileDesc *fd = nsnull;
- fd = OpenFile();
- if (fd) {
- rv = jis->InitFile(&mZip, item, fd);
- // |jis| now owns |fd|
- } else {
- rv = NS_ERROR_FAILURE;
- }
+ rv = jis->InitFile(mZip.GetFD(item), item);
}
if (NS_FAILED(rv)) {
NS_RELEASE(*result);
}
return rv;
}
//----------------------------------------------
@@ -1108,23 +1095,19 @@ nsZipReaderCache::GetZip(nsIFile* zipFil
#ifdef ZIP_CACHE_HIT_RATE
mZipCacheLookups++;
#endif
nsCAutoString path;
rv = zipFile->GetNativePath(path);
if (NS_FAILED(rv)) return rv;
- PRInt64 Mtime;
- rv = zipFile->GetLastModifiedTime(&Mtime);
- if (NS_FAILED(rv)) return rv;
-
nsCStringKey key(path);
nsJAR* zip = static_cast<nsJAR*>(static_cast<nsIZipReader*>(mZips.Get(&key))); // AddRefs
- if (zip && Mtime == zip->GetMtime()) {
+ if (zip) {
#ifdef ZIP_CACHE_HIT_RATE
mZipCacheHits++;
#endif
zip->ClearReleaseTime();
}
else {
if (zip) {
antiLockZipGrip = zip;
--- a/modules/libjar/nsJAR.h
+++ b/modules/libjar/nsJAR.h
@@ -128,20 +128,16 @@ class nsJAR : public nsIZipReader, publi
void ClearReleaseTime() {
mReleaseTime = PR_INTERVAL_NO_TIMEOUT;
}
void SetZipReaderCache(nsZipReaderCache* cache) {
mCache = cache;
}
- PRInt64 GetMtime() {
- return mMtime;
- }
-
protected:
//-- Private data members
nsCOMPtr<nsIFile> mZipFile; // The zip/jar file on disk
nsZipArchive mZip; // The underlying zip archive
nsObjectHashtable mManifestData; // Stores metadata for each entry
PRBool mParsedManifest; // True if manifest has been parsed
nsCOMPtr<nsIPrincipal> mPrincipal; // The entity which signed this file
PRInt16 mGlobalStatus; // Global signature verification status
--- a/modules/libjar/nsJARInputStream.cpp
+++ b/modules/libjar/nsJARInputStream.cpp
@@ -18,16 +18,17 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mitch Stoltz <mstoltz@netscape.com>
+ * Taras Glek <tglek@mozilla.com>
*
* 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 MPL, indicate your
@@ -40,45 +41,40 @@
#include "nsJARInputStream.h"
#include "zipstruct.h" // defines ZIP compression codes
#include "nsZipArchive.h"
#include "nsNetUtil.h"
#include "nsEscape.h"
#include "nsIFile.h"
+#include "nsDebug.h"
/*---------------------------------------------
* nsISupports implementation
*--------------------------------------------*/
NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream)
/*----------------------------------------------------------
* nsJARInputStream implementation
*--------------------------------------------------------*/
nsresult
-nsJARInputStream::InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd)
+nsJARInputStream::InitFile(nsZipHandle *aFd, nsZipItem *item)
{
nsresult rv;
-
- // Keep the file handle, even on failure
- mFd = fd;
-
- NS_ENSURE_ARG_POINTER(aZip);
- NS_ENSURE_ARG_POINTER(item);
- NS_ENSURE_ARG_POINTER(fd);
+ NS_ABORT_IF_FALSE(aFd, "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
mClosed = PR_TRUE;
-
// Keep the important bits of nsZipItem only
mInSize = item->size;
-
+
//-- prepare for the compression type
switch (item->compression) {
case STORED:
break;
case DEFLATED:
mInflate = (InflateStruct *) PR_Malloc(sizeof(InflateStruct));
NS_ENSURE_TRUE(mInflate, NS_ERROR_OUT_OF_MEMORY);
@@ -91,39 +87,38 @@ nsJARInputStream::InitFile(nsZipArchive*
mInflate->mOutCrc = crc32(0L, Z_NULL, 0);
break;
default:
return NS_ERROR_NOT_IMPLEMENTED;
}
//-- Set filepointer to start of item
- rv = aZip->SeekToItem(item, mFd);
- NS_ENSURE_SUCCESS(rv, NS_ERROR_FILE_CORRUPTED);
+ mFd.Open(aFd, item->dataOffset, item->size);
// Open for reading
mClosed = PR_FALSE;
mCurPos = 0;
return NS_OK;
}
nsresult
-nsJARInputStream::InitDirectory(nsZipArchive* aZip,
+nsJARInputStream::InitDirectory(nsJAR* aJar,
const nsACString& aJarDirSpec,
const char* aDir)
{
- NS_ENSURE_ARG_POINTER(aZip);
- NS_ENSURE_ARG_POINTER(aDir);
+ NS_ABORT_IF_FALSE(aJar, "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
mClosed = PR_TRUE;
mDirectory = PR_TRUE;
// Keep the zipReader for getting the actual zipItems
- mZip = aZip;
+ mJar = aJar;
nsZipFind *find;
nsresult rv;
// We can get aDir's contents as strings via FindEntries
// with the following pattern (see nsIZipReader.findEntries docs)
// assuming dirName is properly escaped:
//
// dirName + "?*~" + dirName + "?*/?*"
nsDependentCString dirName(aDir);
@@ -151,17 +146,17 @@ nsJARInputStream::InitDirectory(nsZipArc
// fall through
default:
escDirName.Append(*curr);
}
++curr;
}
nsCAutoString pattern = escDirName + NS_LITERAL_CSTRING("?*~") +
escDirName + NS_LITERAL_CSTRING("?*/?*");
- rv = aZip->FindInit(pattern.get(), &find);
+ rv = mJar->mZip.FindInit(pattern.get(), &find);
if (NS_FAILED(rv)) return rv;
const char *name;
while ((rv = find->FindNext( &name )) == NS_OK) {
// No need to copy string, just share the one from nsZipArchive
mArray.AppendElement(nsDependentCString(name));
}
delete find;
@@ -215,37 +210,35 @@ nsJARInputStream::Read(char* aBuffer, PR
rv = ReadDirectory(aBuffer, aCount, aBytesRead);
} else {
if (mInflate) {
rv = ContinueInflate(aBuffer, aCount, aBytesRead);
} else {
PRInt32 bytesRead = 0;
aCount = PR_MIN(aCount, mInSize - mCurPos);
if (aCount) {
- bytesRead = PR_Read(mFd, aBuffer, aCount);
+ bytesRead = mFd.Read(aBuffer, aCount);
if (bytesRead < 0)
return NS_ERROR_FILE_CORRUPTED;
mCurPos += bytesRead;
- if (bytesRead != aCount) {
+ if ((PRUint32)bytesRead != aCount) {
// file is truncated or was lying about size, we're done
- PR_Close(mFd);
- mFd = nsnull;
+ Close();
return NS_ERROR_FILE_CORRUPTED;
}
}
*aBytesRead = bytesRead;
}
-
// be aggressive about closing!
// note that sometimes, we will close mFd before we've finished
// deflating - this is because zlib buffers the input
// So, don't free the ReadBuf/InflateStruct yet.
- if (mCurPos >= mInSize && mFd) {
- PR_Close(mFd);
- mFd = nsnull;
+ // It is ok to close the fd multiple times (also in nsJARInputStream::Close())
+ if (mCurPos >= mInSize) {
+ mFd.Close();
}
}
return rv;
}
NS_IMETHODIMP
nsJARInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
{
@@ -260,21 +253,18 @@ nsJARInputStream::IsNonBlocking(PRBool *
*aNonBlocking = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsJARInputStream::Close()
{
PR_FREEIF(mInflate);
- if (mFd) {
- PR_Close(mFd);
- mFd = nsnull;
- }
mClosed = PR_TRUE;
+ mFd.Close();
return NS_OK;
}
nsresult
nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount,
PRUint32* aBytesRead)
{
// No need to check the args, ::Read did that, but assert them at least
@@ -292,18 +282,17 @@ nsJARInputStream::ContinueInflate(char*
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);
- NS_ASSERTION(mFd, "File handle missing");
- PRInt32 bytesRead = PR_Read(mFd, mInflate->mReadBuf, bytesToRead);
+ 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;
@@ -357,17 +346,17 @@ nsJARInputStream::ReadDirectory(char* aB
for ( ;aCount > mBuffer.Length(); mArrPos++) {
// have we consumed all the directory contents?
if (arrayLen <= mArrPos)
break;
const char * entryName = mArray[mArrPos].get();
PRUint32 entryNameLen = mArray[mArrPos].Length();
- nsZipItem* ze = mZip->GetItem(entryName);
+ nsZipItem* ze = mJar->mZip.GetItem(entryName);
NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
// Last Modified Time
PRExplodedTime tm;
PR_ExplodeTime(GetModTime(ze->date, ze->time), PR_GMTParameters, &tm);
char itemLastModTime[65];
PR_FormatTimeUSEnglish(itemLastModTime,
sizeof(itemLastModTime),
--- a/modules/libjar/nsJARInputStream.h
+++ b/modules/libjar/nsJARInputStream.h
@@ -48,54 +48,55 @@
* Class nsJARInputStream declaration. This class defines the type of the
* object returned by calls to nsJAR::GetInputStream(filename) for the
* purpose of reading a file item out of a JAR file.
*------------------------------------------------------------------------*/
class nsJARInputStream : public nsIInputStream
{
public:
nsJARInputStream() :
- mFd(nsnull), mInSize(0), mCurPos(0),
- mInflate(nsnull), mDirectory(0), mClosed(PR_FALSE) { }
-
- ~nsJARInputStream() { Close(); }
+ mInSize(0), mCurPos(0), mInflate(nsnull), mDirectory(0), mClosed(PR_FALSE)
+ { }
+
+ ~nsJARInputStream() {
+ Close();
+ }
NS_DECL_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
// takes ownership of |fd|, even on failure
- nsresult InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd);
+ nsresult InitFile(nsZipHandle *aFd, nsZipItem *item);
- nsresult InitDirectory(nsZipArchive* aZip,
+ nsresult InitDirectory(nsJAR *aJar,
const nsACString& aJarDirSpec,
const char* aDir);
private:
- PRFileDesc* mFd; // My own file handle, for reading
PRUint32 mInSize; // Size in original file
PRUint32 mCurPos; // Current position in input
struct InflateStruct {
PRUint32 mOutSize; // inflated size
PRUint32 mInCrc; // CRC as provided by the zipentry
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 */
- nsZipArchive* mZip; // the zipReader
+ nsRefPtr<nsJAR> mJar; // string reference to zipreader
PRUint32 mNameLen; // length of dirname
nsCAutoString mBuffer; // storage for generated text of stream
PRUint32 mArrPos; // current position within mArray
nsTArray<nsCString> mArray; // array of names in (zip) directory
-
- PRPackedBool mDirectory;
- PRPackedBool mClosed; // Whether the stream is closed
+ PRPackedBool mDirectory; // is this a directory?
+ PRPackedBool mClosed; // Whether the stream is closed
+ nsSeekableZipHandle mFd; // handle for reading
nsresult ContinueInflate(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);
nsresult ReadDirectory(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead);
PRUint32 CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount);
};
#endif /* nsJARINPUTSTREAM_h__ */
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -21,16 +21,17 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Veditz <dveditz@netscape.com>
* Samir Gehani <sgehani@netscape.com>
* Mitch Stoltz <mstoltz@netscape.com>
* Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
* Jeff Walden <jwalden+code@mit.edu>
+ * Taras Glek <tglek@mozilla.com>
*
* 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 MPL, indicate your
@@ -44,27 +45,29 @@
/*
* This module implements a simple archive extractor for the PKZIP format.
*
* The underlying nsZipArchive is NOT thread-safe. Do not pass references
* or pointers to it across thread boundaries.
*/
-#include "nsWildCard.h"
-#include "nscore.h"
-#include "prmem.h"
-#include "prio.h"
-#include "plstr.h"
-#include "prlog.h"
#define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
#define READTYPE PRInt32
#include "zlib.h"
#include "nsISupportsUtils.h"
#include "nsRecyclingAllocator.h"
+#include "prio.h"
+#include "plstr.h"
+#include "prlog.h"
+#include "stdlib.h"
+#include "nsWildCard.h"
+#include "zipfile.h"
+#include "zipstruct.h"
+#include "nsZipArchive.h"
/**
* Globals
*
* Global allocator used with zlib. Destroyed in module shutdown.
*/
#define NBUCKETS 6
#define BY4ALLOC_ITEMS 320
@@ -200,35 +203,87 @@ nsresult gZlibInit(z_stream *zs)
zs->opaque = gZlibAllocator;
}
int zerr = inflateInit2(zs, -MAX_WBITS);
if (zerr != Z_OK) return ZIP_ERR_MEMORY;
return ZIP_OK;
}
+nsZipHandle::nsZipHandle()
+ : mFd(nsnull)
+ , mFileData(nsnull)
+ , mLen(0)
+ , mMap(nsnull)
+ , mRefCnt(1)
+{
+}
+
+NS_IMPL_THREADSAFE_ADDREF(nsZipHandle)
+NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
+
+nsresult nsZipHandle::Init(PRFileDesc *fd, nsZipHandle **ret)
+{
+ PRInt64 size = PR_Available64(fd);
+ if (size >= PR_INT32_MAX)
+ return NS_ERROR_FILE_TOO_BIG;
+
+ PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
+
+ if (!map)
+ return NS_ERROR_FAILURE;
+
+ nsZipHandle *handle = new nsZipHandle();
+ if (!handle)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ handle->mFd = fd;
+ handle->mMap = map;
+ handle->mLen = (PRUint32) size;
+ handle->mFileData = (PRUint8*) PR_MemMap(map, 0, handle->mLen);
+ *ret = handle;
+ 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()
+{
+ if (mFd) {
+ PR_MemUnmap(mFileData, mLen);
+ PR_CloseFileMap(mMap);
+ PR_Close(mFd);
+ mFd = 0;
+ }
+}
+
//***********************************************************
// nsZipArchive -- public methods
//***********************************************************
//---------------------------------------------
// nsZipArchive::OpenArchive
//---------------------------------------------
nsresult nsZipArchive::OpenArchive(PRFileDesc * fd)
{
- if (!fd)
- return ZIP_ERR_PARAM;
+ nsresult rv = nsZipHandle::Init(fd, getter_AddRefs(mFd));
+ if (NS_FAILED(rv))
+ return rv;
// Initialize our arena
PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
- //-- Keep the filedescriptor for further reading...
- mFd = fd;
-
//-- get table of contents for archive
return BuildFileList();
}
//---------------------------------------------
// nsZipArchive::Test
//---------------------------------------------
nsresult nsZipArchive::Test(const char *aEntryName)
@@ -274,21 +329,18 @@ nsresult nsZipArchive::CloseArchive()
// We don't need to delete each of the nsZipItem as the memory for
// the zip item and the filename it holds are both allocated from the Arena.
// Hence, destroying the Arena is like destroying all the memory
// for all the nsZipItem in one shot. But if the ~nsZipItem is doing
// 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
for (int i = 0; i < ZIP_TABSIZE; i++) {
mFiles[i] = 0;
- }
- if (mFd) {
- PR_Close(mFd);
- mFd = 0;
}
+ mFd = NULL;
mBuiltSynthetics = PR_FALSE;
return ZIP_OK;
}
//---------------------------------------------
// nsZipArchive::GetItem
//---------------------------------------------
nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
@@ -329,30 +381,34 @@ nsresult nsZipArchive::ExtractFile(nsZip
if (!mFd)
return ZIP_ERR_GENERAL;
// Directory extraction is handled in nsJAR::Extract,
// so the item to be extracted should never be a directory
PR_ASSERT(!item->isDirectory);
//-- move to the start of file's data
- if (SeekToItem(item, mFd) != ZIP_OK)
+ if (MaybeReadItem(item) != ZIP_OK)
+ return ZIP_ERR_CORRUPT;
+
+ nsSeekableZipHandle fd;
+ if (!fd.Open(mFd.get(), item->headerOffset, item->size))
return ZIP_ERR_CORRUPT;
nsresult rv;
//-- extract the file using the appropriate method
switch(item->compression)
{
case STORED:
- rv = CopyItemToDisk(item->size, item->crc32, aFd);
+ rv = CopyItemToDisk(item->size, item->crc32, fd, aFd);
break;
case DEFLATED:
- rv = InflateItem(item, aFd);
+ rv = InflateItem(item, fd, aFd);
break;
default:
//-- unsupported compression type
rv = ZIP_ERR_UNSUPPORTED;
}
//-- delete the file on errors, or resolve symlink if needed
@@ -504,74 +560,64 @@ nsZipItem* nsZipArchive::CreateZipItem(P
return (nsZipItem*)mem;
}
//---------------------------------------------
// nsZipArchive::BuildFileList
//---------------------------------------------
nsresult nsZipArchive::BuildFileList()
{
- PRUint8 buf[4*BR_BUF_SIZE];
-
+ PRUint8 *buf;
//-----------------------------------------------------------------------
// locate the central directory via the End record
//-----------------------------------------------------------------------
//-- get archive size using end pos
- PRInt32 pos = PR_Seek(mFd, 0, PR_SEEK_END);
- if (pos <= 0)
- return ZIP_ERR_CORRUPT;
+ PRInt32 pos = (PRInt32) mFd->mLen;
- PRBool bEndsigFound = PR_FALSE;
- while (!bEndsigFound)
+ PRInt32 central = -1;
+ while (central == -1)
{
//-- read backwards in 1K-sized chunks (unless file is less than 1K)
PRInt32 bufsize = pos > BR_BUF_SIZE ? BR_BUF_SIZE : pos;
pos -= bufsize;
- if (!ZIP_Seek(mFd, pos, PR_SEEK_SET))
- return ZIP_ERR_CORRUPT;
-
- if (PR_Read(mFd, buf, bufsize) != (READTYPE)bufsize)
- return ZIP_ERR_CORRUPT;
+ buf = mFd->mFileData + pos;
//-- scan for ENDSIG
PRUint8 *endp = buf + bufsize;
for (endp -= ZIPEND_SIZE; endp >= buf; endp--)
{
if (xtolong(endp) == ENDSIG)
{
//-- Seek to start of central directory
- PRInt32 central = xtolong(((ZipEnd *) endp)->offset_central_dir);
- if (!ZIP_Seek(mFd, central, PR_SEEK_SET))
- return ZIP_ERR_CORRUPT;
-
- bEndsigFound = PR_TRUE;
+ central = xtolong(((ZipEnd *) endp)->offset_central_dir);
break;
}
}
- if (bEndsigFound)
+ if (central != -1)
break;
if (pos <= 0)
//-- We're at the beginning of the file, and still no sign
//-- of the end signature. File must be corrupted!
return ZIP_ERR_CORRUPT;
//-- backward read must overlap ZipEnd length
pos += ZIPEND_SIZE;
} /* while looking for end signature */
//-------------------------------------------------------
// read the central directory headers
//-------------------------------------------------------
- PRInt32 byteCount = PR_Read(mFd, &buf, sizeof(buf));
+ PRInt32 byteCount = mFd->mLen - central;
+ buf = mFd->mFileData + central;
pos = 0;
PRUint32 sig = xtolong(buf);
while (sig == CENTRALSIG) {
//-- make sure we've read enough
if (byteCount - pos < ZIPCENTRAL_SIZE)
return ZIP_ERR_CORRUPT;
//-------------------------------------------------------
@@ -608,41 +654,21 @@ nsresult nsZipArchive::BuildFileList()
item->mode = ExtractMode(central->external_attributes);
#if defined(XP_UNIX) || defined(XP_BEOS)
// Check if item is a symlink
item->isSymlink = IsSymlink(central->external_attributes);
#endif
pos += ZIPCENTRAL_SIZE;
-
- //-------------------------------------------------------
- // Make sure that remainder of this record (name, comments, extra)
- // and the next ZipCentral is all in the buffer
- //-------------------------------------------------------
- PRInt32 leftover = byteCount - pos;
- if (leftover < (namelen + extralen + commentlen + ZIPCENTRAL_SIZE)) {
- //-- not enough data left to process at top of loop.
- //-- move leftover and read more
- memcpy(buf, buf+pos, leftover);
- byteCount = leftover + PR_Read(mFd, buf+leftover, sizeof(buf)-leftover);
- pos = 0;
-
- if (byteCount < (namelen + extralen + commentlen + sizeof(sig))) {
- // truncated file
- return ZIP_ERR_CORRUPT;
- }
- }
-
//-------------------------------------------------------
// get the item name
//-------------------------------------------------------
memcpy(item->name, buf+pos, namelen);
item->name[namelen] = 0;
-
//-- an item whose name ends with '/' is a directory
item->isDirectory = ('/' == item->name[namelen - 1]);
//-- add item to file table
//-- note that an explicit entry for a directory will override
//-- a fake entry created for that directory (as in the case
//-- of processing foo/bar.txt and then foo/) -- this will
//-- preserve an explicit directory's metadata at the cost of
@@ -753,63 +779,66 @@ nsresult nsZipArchive::BuildSynthetics()
diritem->next = mFiles[hash];
mFiles[hash] = diritem;
} /* end processing of dirs in item's name */
}
}
return ZIP_OK;
}
+nsZipHandle* nsZipArchive::GetFD(nsZipItem* aItem)
+{
+ if (!mFd || !MaybeReadItem(aItem))
+ return NULL;
+ return mFd.get();
+}
//---------------------------------------------
-// nsZipArchive::SeekToItem
+// nsZipArchive::MaybeReadItem
//---------------------------------------------
-nsresult nsZipArchive::SeekToItem(nsZipItem* aItem, PRFileDesc* aFd)
+bool nsZipArchive::MaybeReadItem(nsZipItem* aItem)
{
PR_ASSERT (aItem);
//-- the first time an item is used we need to calculate its offset
if (!aItem->hasDataOffset)
{
//-- read local header to get variable length values and calculate
//-- 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 (!ZIP_Seek(aFd, aItem->headerOffset, PR_SEEK_SET))
- return ZIP_ERR_CORRUPT;
+ if (!mFd || !mFd->mLen > aItem->headerOffset + ZIPLOCAL_SIZE)
+ return false;
- ZipLocal Local;
- if ((PR_Read(aFd, (char*)&Local, ZIPLOCAL_SIZE) != (READTYPE) ZIPLOCAL_SIZE) ||
- (xtolong(Local.signature) != LOCALSIG))
+ ZipLocal *Local = (ZipLocal*)(mFd->mFileData + aItem->headerOffset);
+ //check limits here
+ if ((xtolong(Local->signature) != LOCALSIG))
{
//-- read error or local header not found
- return ZIP_ERR_CORRUPT;
+ return false;
}
aItem->dataOffset = aItem->headerOffset +
ZIPLOCAL_SIZE +
- xtoint(Local.filename_len) +
- xtoint(Local.extrafield_len);
+ xtoint(Local->filename_len) +
+ xtoint(Local->extrafield_len);
aItem->hasDataOffset = PR_TRUE;
}
- //-- move to start of file in archive
- if (!ZIP_Seek(aFd, aItem->dataOffset, PR_SEEK_SET))
- return ZIP_ERR_CORRUPT;
-
- return ZIP_OK;
+ return true;
}
//---------------------------------------------
// nsZipArchive::CopyItemToDisk
//---------------------------------------------
nsresult
-nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, PRFileDesc* outFD)
+nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc,
+ 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;
char buf[ZIP_BUFLEN];
@@ -817,17 +846,17 @@ nsZipArchive::CopyItemToDisk(PRUint32 it
//-- initialize crc
crc = crc32(0L, Z_NULL, 0);
//-- copy chunks until file is done
for (pos = 0; pos < itemSize; pos += chunk)
{
chunk = (itemSize - pos < ZIP_BUFLEN) ? (itemSize - pos) : ZIP_BUFLEN;
- if (PR_Read(mFd, buf, chunk) != (READTYPE)chunk)
+ 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);
@@ -844,17 +873,17 @@ nsZipArchive::CopyItemToDisk(PRUint32 it
return ZIP_OK;
}
//---------------------------------------------
// nsZipArchive::InflateItem
//---------------------------------------------
-nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, PRFileDesc* outFD)
+nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD)
/*
* This function inflates 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'.
*/
{
PR_ASSERT(aItem);
@@ -882,17 +911,17 @@ nsresult nsZipArchive::InflateItem(const
PRBool bWrote= PR_FALSE;
if (zs.avail_in == 0 && zs.total_in < size)
{
//-- no data to inflate yet still more in file:
//-- read another chunk of compressed data
PRUint32 chunk = (size-zs.total_in < ZIP_BUFLEN) ? size-zs.total_in : ZIP_BUFLEN;
- if (PR_Read(mFd, inbuf, chunk) != (READTYPE)chunk)
+ if (fd.Read(inbuf, chunk) != (READTYPE)chunk)
{
//-- unexpected end of data
status = ZIP_ERR_CORRUPT;
break;
}
zs.next_in = inbuf;
zs.avail_in = chunk;
@@ -961,18 +990,17 @@ cleanup:
return status;
}
//------------------------------------------
// nsZipArchive constructor and destructor
//------------------------------------------
nsZipArchive::nsZipArchive() :
- mFd(0),
- mBuiltSynthetics(PR_FALSE)
+ mBuiltSynthetics(PR_FALSE)
{
MOZ_COUNT_CTOR(nsZipArchive);
// initialize the table to NULL
memset(mFiles, 0, sizeof mFiles);
}
nsZipArchive::~nsZipArchive()
@@ -1070,9 +1098,8 @@ static PRUint16 ExtractMode(unsigned cha
*
*/
static PRBool IsSymlink(unsigned char *ll)
{
return ((xtoint(ll+2) & S_IFMT) == S_IFLNK);
}
#endif
-
--- a/modules/libjar/nsZipArchive.h
+++ b/modules/libjar/nsZipArchive.h
@@ -19,16 +19,17 @@
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Veditz <dveditz@netscape.com>
* Samir Gehani <sgehani@netscape.com>
* Mitch Stoltz <mstoltz@netscape.com>
+ * Taras Glek <tglek@mozilla.com>
*
* 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 MPL, indicate your
@@ -48,16 +49,17 @@
// Keep this odd. The -1 is significant.
#define ZIP_BUFLEN (4 * 1024 - 1)
#define PL_ARENA_CONST_ALIGN_MASK 7
#include "plarena.h"
#define ZIP_Seek(fd,p,m) (PR_Seek((fd),((PROffset32)p),(m))==((PROffset32)p))
#include "zlib.h"
+#include "nsAutoPtr.h"
class nsZipFind;
class nsZipReadState;
class nsZipItemMetadata;
struct PRFileDesc;
/**
@@ -106,16 +108,18 @@ struct nsZipItem
and no foo/ entry is synthetic */
#if defined(XP_UNIX) || defined(XP_BEOS)
PRPackedBool isSymlink : 1;
#endif
char name[1]; // actually, bigger than 1
};
+class nsZipHandle;
+class nsSeekableZipHandle;
/**
* nsZipArchive -- a class for reading the PKZIP file format.
*
*/
class nsZipArchive
{
friend class nsZipFind;
@@ -181,46 +185,134 @@ public:
* (may be NULL to find all files in archive)
* @param aFind a pointer to a pointer to a structure used
* in FindNext. In the case of an error this
* will be set to NULL.
* @return status code
*/
PRInt32 FindInit(const char * aPattern, nsZipFind** aFind);
- /**
- * Moves the filepointer aFd to the start of data of the aItem.
- * @param aItem Pointer to nsZipItem
- * @param aFd The filepointer to move
+ /* Gets an undependent handle to the jar
+ * Also ensures that aItem is fully filled
*/
- nsresult SeekToItem(nsZipItem* aItem, PRFileDesc* aFd);
+ nsZipHandle* GetFD(nsZipItem* aItem);
private:
//--- private members ---
nsZipItem* mFiles[ZIP_TABSIZE];
PLArenaPool mArena;
- // Used for central directory reading, and for Test and Extract
- PRFileDesc *mFd;
+ /**
+ * 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
PRPackedBool mBuiltSynthetics;
+ // file handle
+ nsRefPtr<nsZipHandle> mFd;
//--- private methods ---
nsZipArchive& operator=(const nsZipArchive& rhs); // prevent assignments
nsZipArchive(const nsZipArchive& rhs); // prevent copies
nsZipItem* CreateZipItem(PRUint16 namelen);
nsresult BuildFileList();
nsresult BuildSynthetics();
- nsresult CopyItemToDisk(PRUint32 size, PRUint32 crc, PRFileDesc* outFD);
- nsresult InflateItem(const nsZipItem* aItem, PRFileDesc* outFD);
+ nsresult CopyItemToDisk(PRUint32 size, PRUint32 crc, nsSeekableZipHandle &fd, PRFileDesc* outFD);
+ nsresult InflateItem(const nsZipItem* aItem, nsSeekableZipHandle &fd, PRFileDesc* outFD);
+};
+
+class nsZipHandle {
+friend class nsZipArchive;
+friend class nsSeekableZipHandle;
+public:
+ 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);
+
+ nsrefcnt AddRef(void);
+ nsrefcnt Release(void);
+
+protected:
+ PRFileDesc *mFd; // OS file-descriptor
+ PRUint8 *mFileData; // pointer to mmaped file
+ PRUint32 mLen; // length of file and memory mapped area
+
+private:
+ nsZipHandle();
+ ~nsZipHandle();
+
+ PRFileMap *mMap; // nspr datastructure for mmap
+ 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
};
/**
* nsZipFind
*
* a helper class for nsZipArchive, representing a search
*/
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/unit/test_jarinput_stream_zipreader_reference.js
@@ -0,0 +1,66 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** 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
+ * Taras Glek <tglek@mozilla.com>
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * 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 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 ***** */
+
+function wrapInputStream(input)
+{
+ var nsIScriptableInputStream = Components.interfaces.nsIScriptableInputStream;
+ var factory = Components.classes["@mozilla.org/scriptableinputstream;1"];
+ var wrapper = factory.createInstance(nsIScriptableInputStream);
+ wrapper.init(input);
+ return wrapper;
+}
+
+// Check that files can be read from after closing zipreader
+function run_test() {
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+
+ // the build script have created the zip we can test on in the current dir.
+ var file = do_get_file("data/test_bug333423.zip");
+
+ var zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ zipreader.open(file);
+ var entries = zipreader.findEntries(null);
+ var stream = wrapInputStream(zipreader.getInputStream("modules/libjar/test/Makefile.in"))
+ var dirstream= wrapInputStream(zipreader.getInputStream("modules/libjar/test/"))
+ zipreader.close();
+ zipreader = null;
+ Components.utils.forceGC();
+ do_check_true(stream.read(1024).length > 0);
+ do_check_true(dirstream.read(100).length > 0);
+}
+