Bug 504864 - mmap io for JARs; r=benjamin

This commit is contained in:
Taras Glek 2009-08-08 12:07:39 +02:00
parent 7b5cd072e9
commit ed2f94b3a5
7 changed files with 310 additions and 156 deletions

View File

@ -122,7 +122,6 @@ nsJAR::nsJAR(): mManifestData(nsnull, nsnull, DeleteManifestEntry, nsnull, 10),
mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
mCache(nsnull),
mLock(nsnull),
mMtime(0),
mTotalItemsInManifest(0)
{
}
@ -167,8 +166,6 @@ nsJAR::Open(nsIFile* 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);
@ -176,7 +173,7 @@ nsJAR::Open(nsIFile* zipFile)
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;
@ -340,19 +337,9 @@ nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
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);
@ -1113,13 +1100,9 @@ nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
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

View File

@ -133,10 +133,6 @@ class nsJAR : public nsIZipReader, public nsIJAR
mCache = cache;
}
PRInt64 GetMtime() {
return mMtime;
}
protected:
//-- Private data members
nsCOMPtr<nsIFile> mZipFile; // The zip/jar file on disk

View File

@ -23,6 +23,7 @@
*
* 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
@ -45,6 +46,7 @@
#include "nsNetUtil.h"
#include "nsEscape.h"
#include "nsIFile.h"
#include "nsDebug.h"
/*---------------------------------------------
* nsISupports implementation
@ -57,23 +59,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream)
*--------------------------------------------------------*/
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:
@ -96,8 +92,7 @@ nsJARInputStream::InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd)
}
//-- 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;
@ -106,19 +101,19 @@ nsJARInputStream::InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd)
}
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
@ -156,7 +151,7 @@ nsJARInputStream::InitDirectory(nsZipArchive* aZip,
}
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;
@ -220,27 +215,25 @@ nsJARInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
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;
@ -265,11 +258,8 @@ NS_IMETHODIMP
nsJARInputStream::Close()
{
PR_FREEIF(mInflate);
if (mFd) {
PR_Close(mFd);
mFd = nsnull;
}
mClosed = PR_TRUE;
mFd.Close();
return NS_OK;
}
@ -297,8 +287,7 @@ nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount,
// 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;
@ -362,7 +351,7 @@ nsJARInputStream::ReadDirectory(char* aBuffer, PRUint32 aCount, PRUint32 *aBytes
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

View File

@ -53,23 +53,24 @@ 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
@ -83,14 +84,14 @@ class nsJARInputStream : public nsIInputStream
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);

View File

@ -26,6 +26,7 @@
* 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
@ -49,17 +50,19 @@
*/
#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
@ -205,6 +208,60 @@ nsresult gZlibInit(z_stream *zs)
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
//***********************************************************
@ -215,15 +272,13 @@ nsresult gZlibInit(z_stream *zs)
//---------------------------------------------
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();
}
@ -279,11 +334,8 @@ nsresult nsZipArchive::CloseArchive()
// 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;
}
@ -334,7 +386,11 @@ nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
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;
@ -343,11 +399,11 @@ nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
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:
@ -509,29 +565,22 @@ nsZipItem* nsZipArchive::CreateZipItem(PRUint16 namelen)
//---------------------------------------------
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;
@ -540,16 +589,12 @@ nsresult nsZipArchive::BuildFileList()
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)
@ -566,7 +611,8 @@ nsresult nsZipArchive::BuildFileList()
//-------------------------------------------------------
// 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) {
@ -613,31 +659,11 @@ nsresult nsZipArchive::BuildFileList()
#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]);
@ -758,11 +784,17 @@ nsresult nsZipArchive::BuildSynthetics()
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);
@ -775,36 +807,33 @@ nsresult nsZipArchive::SeekToItem(nsZipItem* aItem, PRFileDesc* aFd)
//-- 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
@ -822,7 +851,7 @@ nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, PRFileDesc* ou
{
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;
@ -849,7 +878,7 @@ nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, PRFileDesc* ou
//---------------------------------------------
// 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
@ -887,7 +916,7 @@ nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, PRFileDesc* outFD)
//-- 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;
@ -966,8 +995,7 @@ cleanup:
//------------------------------------------
nsZipArchive::nsZipArchive() :
mFd(0),
mBuiltSynthetics(PR_FALSE)
mBuiltSynthetics(PR_FALSE)
{
MOZ_COUNT_CTOR(nsZipArchive);
@ -1075,4 +1103,3 @@ static PRBool IsSymlink(unsigned char *ll)
return ((xtoint(ll+2) & S_IFMT) == S_IFLNK);
}
#endif

View File

@ -24,6 +24,7 @@
* 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
@ -53,6 +54,7 @@
#define ZIP_Seek(fd,p,m) (PR_Seek((fd),((PROffset32)p),(m))==((PROffset32)p))
#include "zlib.h"
#include "nsAutoPtr.h"
class nsZipFind;
class nsZipReadState;
@ -111,6 +113,8 @@ struct nsZipItem
char name[1]; // actually, bigger than 1
};
class nsZipHandle;
class nsSeekableZipHandle;
/**
* nsZipArchive -- a class for reading the PKZIP file format.
*
@ -186,12 +190,10 @@ public:
*/
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 ---
@ -199,12 +201,18 @@ private:
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
@ -214,8 +222,92 @@ private:
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
};

View File

@ -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);
}