Bug 504864 - mmap io for JARs; r=benjamin
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user