Bug 1744043: Clean up nsJAR r=nika,valentin,extension-reviewers,robwu

Differential Revision: https://phabricator.services.mozilla.com/D132794
This commit is contained in:
Randell Jesup 2022-03-24 13:54:56 +00:00
parent 7a9646220a
commit 703bd76404
12 changed files with 259 additions and 268 deletions

View File

@ -3962,10 +3962,9 @@ nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
nsresult rv;
// Open Jar file to get related attributes of target file.
RefPtr<nsZipArchive> zip = new nsZipArchive();
rv = zip->OpenArchive(aJarFile);
if (NS_FAILED(rv)) {
return rv;
RefPtr<nsZipArchive> zip = nsZipArchive::OpenArchive(aJarFile);
if (!zip) {
return NS_ERROR_FAILURE;
}
nsZipItem* zipItem = zip->GetItem(aFile.get());
if (!zipItem) {

View File

@ -16,7 +16,7 @@ interface nsIUTF8StringEnumerator;
interface nsIInputStream;
interface nsIFile;
[scriptable, uuid(fad6f72f-13d8-4e26-9173-53007a4afe71)]
[scriptable, builtinclass, uuid(fad6f72f-13d8-4e26-9173-53007a4afe71)]
interface nsIZipEntry : nsISupports
{
/**
@ -24,23 +24,23 @@ interface nsIZipEntry : nsISupports
* their meanings are defined in the zip file specification at
* http://www.pkware.com/business_and_developers/developer/appnote/
*/
readonly attribute unsigned short compression;
[infallible] readonly attribute unsigned short compression;
/**
* The compressed size of the data in the item.
*/
readonly attribute unsigned long size;
[infallible] readonly attribute unsigned long size;
/**
* The uncompressed size of the data in the item.
*/
readonly attribute unsigned long realSize;
[infallible] readonly attribute unsigned long realSize;
/**
* The CRC-32 hash of the file in the entry.
*/
readonly attribute unsigned long CRC32;
[infallible] readonly attribute unsigned long CRC32;
/**
* True if the name of the entry ends with '/' and false otherwise.
*/
readonly attribute boolean isDirectory;
[infallible] readonly attribute boolean isDirectory;
/**
* The time at which this item was last modified.
*/
@ -55,11 +55,11 @@ interface nsIZipEntry : nsISupports
* this attribute will be false for the nsIZipEntry for that directory.
* It is impossible for a file to be synthetic.
*/
readonly attribute boolean isSynthetic;
[infallible] readonly attribute boolean isSynthetic;
/**
* The UNIX style file permissions of this item.
*/
readonly attribute unsigned long permissions;
[infallible] readonly attribute unsigned long permissions;
};
[scriptable, uuid(9ba4ef54-e0a0-4f65-9d23-128482448885)]

View File

@ -26,13 +26,9 @@ using namespace mozilla;
// The following initialization makes a guess of 10 entries per jarfile.
nsJAR::nsJAR()
: mZip(new nsZipArchive()),
mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
mCache(nullptr),
: mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
mLock("nsJAR::mLock"),
mMtime(0),
mOpened(false),
mSkipArchiveClosing(false) {}
mCache(nullptr) {}
nsJAR::~nsJAR() { Close(); }
@ -50,7 +46,7 @@ MozExternalRefCountType nsJAR::Release(void) {
if (mRefCnt == 2) { // don't use a lock too frequently
// Use a mutex here to guarantee mCache is not racing and the target
// instance is still valid to increase ref-count.
MutexAutoLock lock(mLock);
RecursiveMutexAutoLock lock(mLock);
cache = mCache;
mCache = nullptr;
}
@ -79,76 +75,83 @@ MozExternalRefCountType nsJAR::Release(void) {
NS_IMETHODIMP
nsJAR::Open(nsIFile* zipFile) {
NS_ENSURE_ARG_POINTER(zipFile);
if (mOpened) return NS_ERROR_FAILURE; // Already open!
RecursiveMutexAutoLock lock(mLock);
if (mZip) return NS_ERROR_FAILURE; // Already open!
mZipFile = zipFile;
mOuterZipEntry.Truncate();
mOpened = true;
// The omnijar is special, it is opened early on and closed late
// this avoids reopening it
RefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile);
if (zip) {
mZip = zip;
mSkipArchiveClosing = true;
return NS_OK;
if (!zip) {
zip = nsZipArchive::OpenArchive(zipFile);
}
return mZip->OpenArchive(zipFile);
mZip = zip;
return mZip ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsJAR::OpenInner(nsIZipReader* aZipReader, const nsACString& aZipEntry) {
NS_ENSURE_ARG_POINTER(aZipReader);
if (mOpened) return NS_ERROR_FAILURE; // Already open!
nsresult rv;
NS_ENSURE_ARG_POINTER(aZipReader);
nsCOMPtr<nsIFile> zipFile;
rv = aZipReader->GetFile(getter_AddRefs(zipFile));
NS_ENSURE_SUCCESS(rv, rv);
nsJAR* outerJAR = static_cast<nsJAR*>(aZipReader);
RefPtr<nsZipArchive> innerZip =
mozilla::Omnijar::GetInnerReader(outerJAR->mZipFile, aZipEntry);
mozilla::Omnijar::GetInnerReader(zipFile, aZipEntry);
if (innerZip) {
mOpened = true;
RecursiveMutexAutoLock lock(mLock);
if (mZip) {
return NS_ERROR_FAILURE;
}
mZip = innerZip;
mSkipArchiveClosing = true;
return NS_OK;
}
bool exist;
nsresult rv = aZipReader->HasEntry(aZipEntry, &exist);
rv = aZipReader->HasEntry(aZipEntry, &exist);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND);
rv = aZipReader->GetFile(getter_AddRefs(mZipFile));
NS_ENSURE_SUCCESS(rv, rv);
mOpened = true;
mOuterZipEntry.Assign(aZipEntry);
RefPtr<nsZipHandle> handle;
rv = nsZipHandle::Init(static_cast<nsJAR*>(aZipReader)->mZip.get(),
PromiseFlatCString(aZipEntry).get(),
getter_AddRefs(handle));
if (NS_FAILED(rv)) return rv;
{
nsJAR* outerJAR = static_cast<nsJAR*>(aZipReader);
RecursiveMutexAutoLock outerLock(outerJAR->mLock);
rv = nsZipHandle::Init(outerJAR->mZip.get(),
PromiseFlatCString(aZipEntry).get(),
getter_AddRefs(handle));
NS_ENSURE_SUCCESS(rv, rv);
}
return mZip->OpenArchive(handle);
RecursiveMutexAutoLock lock(mLock);
MOZ_ASSERT(!mZip, "Another thread tried to open this nsJAR racily!");
mZipFile = zipFile.forget();
mOuterZipEntry.Assign(aZipEntry);
mZip = nsZipArchive::OpenArchive(handle);
return mZip ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsJAR::OpenMemory(void* aData, uint32_t aLength) {
NS_ENSURE_ARG_POINTER(aData);
if (mOpened) return NS_ERROR_FAILURE; // Already open!
mOpened = true;
RecursiveMutexAutoLock lock(mLock);
if (mZip) return NS_ERROR_FAILURE; // Already open!
RefPtr<nsZipHandle> handle;
nsresult rv = nsZipHandle::Init(static_cast<uint8_t*>(aData), aLength,
getter_AddRefs(handle));
if (NS_FAILED(rv)) return rv;
return mZip->OpenArchive(handle);
mZip = nsZipArchive::OpenArchive(handle);
return mZip ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsJAR::GetFile(nsIFile** result) {
RecursiveMutexAutoLock lock(mLock);
*result = mZipFile;
NS_IF_ADDREF(*result);
return NS_OK;
@ -156,24 +159,21 @@ nsJAR::GetFile(nsIFile** result) {
NS_IMETHODIMP
nsJAR::Close() {
if (!mOpened) {
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE; // Never opened or already closed.
}
mOpened = false;
if (mSkipArchiveClosing) {
// Reset state, but don't close the omnijar because we did not open it.
mSkipArchiveClosing = false;
mZip = new nsZipArchive();
return NS_OK;
}
return mZip->CloseArchive();
mZip = nullptr;
return NS_OK;
}
NS_IMETHODIMP
nsJAR::Test(const nsACString& aEntryName) {
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
return mZip->Test(
aEntryName.IsEmpty() ? nullptr : PromiseFlatCString(aEntryName).get());
}
@ -182,7 +182,10 @@ NS_IMETHODIMP
nsJAR::Extract(const nsACString& aEntryName, nsIFile* outFile) {
// nsZipArchive and zlib are not thread safe
// we need to use a lock to prevent bug #51267
MutexAutoLock lock(mLock);
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
nsZipItem* item = mZip->GetItem(PromiseFlatCString(aEntryName).get());
NS_ENSURE_TRUE(item, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
@ -219,6 +222,10 @@ nsJAR::Extract(const nsACString& aEntryName, nsIFile* outFile) {
NS_IMETHODIMP
nsJAR::GetEntry(const nsACString& aEntryName, nsIZipEntry** result) {
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get());
NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
@ -230,6 +237,10 @@ nsJAR::GetEntry(const nsACString& aEntryName, nsIZipEntry** result) {
NS_IMETHODIMP
nsJAR::HasEntry(const nsACString& aEntryName, bool* result) {
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
*result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr;
return NS_OK;
}
@ -238,6 +249,10 @@ NS_IMETHODIMP
nsJAR::FindEntries(const nsACString& aPattern,
nsIUTF8StringEnumerator** result) {
NS_ENSURE_ARG_POINTER(result);
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
nsZipFind* find;
nsresult rv = mZip->FindInit(
@ -260,6 +275,10 @@ nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
const nsACString& aEntryName,
nsIInputStream** result) {
NS_ENSURE_ARG_POINTER(result);
RecursiveMutexAutoLock lock(mLock);
if (!mZip) {
return NS_ERROR_FAILURE;
}
// Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case!
nsZipItem* item = nullptr;
@ -277,7 +296,8 @@ nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
if (!item || item->IsDirectory()) {
rv = jis->InitDirectory(this, aJarDirSpec, entry.get());
} else {
rv = jis->InitFile(this, item);
RefPtr<nsZipHandle> fd = mZip->GetFD();
rv = jis->InitFile(fd, mZip->GetData(item), item);
}
if (NS_FAILED(rv)) {
NS_RELEASE(*result);
@ -285,13 +305,27 @@ nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
return rv;
}
nsresult nsJAR::GetJarPath(nsACString& aResult) {
nsresult nsJAR::GetFullJarPath(nsACString& aResult) {
RecursiveMutexAutoLock lock(mLock);
NS_ENSURE_ARG_POINTER(mZipFile);
return mZipFile->GetPersistentDescriptor(aResult);
nsresult rv = mZipFile->GetPersistentDescriptor(aResult);
if (NS_FAILED(rv)) {
return rv;
}
if (mOuterZipEntry.IsEmpty()) {
aResult.InsertLiteral("file:", 0);
} else {
aResult.InsertLiteral("jar:", 0);
aResult.AppendLiteral("!/");
aResult.Append(mOuterZipEntry);
}
return NS_OK;
}
nsresult nsJAR::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc) {
RecursiveMutexAutoLock lock(mLock);
if (!aNSPRFileDesc) {
return NS_ERROR_ILLEGAL_VALUE;
}
@ -530,6 +564,7 @@ nsZipReaderCache::nsZipReaderCache()
NS_IMETHODIMP
nsZipReaderCache::Init(uint32_t cacheSize) {
MutexAutoLock lock(mLock);
mCacheSize = cacheSize;
// Register as a memory pressure observer
@ -769,20 +804,14 @@ nsresult nsZipReaderCache::ReleaseZip(nsJAR* zip) {
// remove from hashtable
nsAutoCString uri;
rv = oldest->GetJarPath(uri);
if (NS_FAILED(rv)) return rv;
if (oldest->mOuterZipEntry.IsEmpty()) {
uri.InsertLiteral("file:", 0);
} else {
uri.InsertLiteral("jar:", 0);
uri.AppendLiteral("!/");
uri.Append(oldest->mOuterZipEntry);
rv = oldest->GetFullJarPath(uri);
if (NS_FAILED(rv)) {
return rv;
}
// Retrieving and removing the JAR must be done without an extra AddRef
// Retrieving and removing the JAR should be done without an extra AddRef
// and Release, or we'll trigger nsJAR::Release's magic refcount 1 case
// an extra time and trigger a deadlock.
// an extra time.
RefPtr<nsJAR> removed;
mZips.Remove(uri, getter_AddRefs(removed));
NS_ASSERTION(removed, "botched");

View File

@ -13,7 +13,7 @@
#include "prinrval.h"
#include "mozilla/Atomics.h"
#include "mozilla/Mutex.h"
#include "mozilla/RecursiveMutex.h"
#include "nsCOMPtr.h"
#include "nsClassHashtable.h"
#include "nsString.h"
@ -53,8 +53,10 @@ class nsJAR final : public nsIZipReader {
NS_DECL_NSIZIPREADER
nsresult GetJarPath(nsACString& aResult);
nsresult GetFullJarPath(nsACString& aResult);
// These access to mReleaseTime, which is locked by nsZipReaderCache's
// mLock, not nsJAR's mLock
PRIntervalTime GetReleaseTime() { return mReleaseTime; }
bool IsReleased() { return mReleaseTime != PR_INTERVAL_NO_TIMEOUT; }
@ -64,29 +66,27 @@ class nsJAR final : public nsIZipReader {
void ClearReleaseTime() { mReleaseTime = PR_INTERVAL_NO_TIMEOUT; }
void SetZipReaderCache(nsZipReaderCache* aCache) {
mozilla::MutexAutoLock lock(mLock);
mozilla::RecursiveMutexAutoLock lock(mLock);
mCache = aCache;
}
nsresult GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc);
protected:
//-- Private data members
nsCOMPtr<nsIFile> mZipFile; // The zip/jar file on disk
nsCString mOuterZipEntry; // The entry in the zip this zip is reading from
RefPtr<nsZipArchive> mZip; // The underlying zip archive
PRIntervalTime mReleaseTime; // used by nsZipReaderCache for flushing entries
nsZipReaderCache*
mCache; // if cached, this points to the cache it's contained in
mozilla::Mutex mLock; // protect mCache and mZip
int64_t mMtime;
bool mOpened;
// true if mZip was adopted from elsewhere and should not be closed by us.
bool mSkipArchiveClosing;
nsresult LoadEntry(const nsACString& aFilename, nsCString& aBuf);
int32_t ReadLine(const char** src);
// used by nsZipReaderCache for flushing entries; access is locked by
// nsZipReaderCache's mLock
PRIntervalTime mReleaseTime;
//-- Private data members, protected by mLock
mozilla::RecursiveMutex mLock;
nsCString mOuterZipEntry; // The entry in the zip this zip is reading from
nsCOMPtr<nsIFile> mZipFile; // The zip/jar file on disk
RefPtr<nsZipArchive> mZip; // The underlying zip archive
nsZipReaderCache*
mCache; // if cached, this points to the cache it's contained in
};
/**
@ -105,14 +105,14 @@ class nsJARItem : public nsIZipEntry {
private:
virtual ~nsJARItem() {}
uint32_t mSize; /* size in original file */
uint32_t mRealsize; /* inflated size */
uint32_t mCrc32;
PRTime mLastModTime;
uint16_t mCompression;
uint32_t mPermissions;
bool mIsDirectory;
bool mIsSynthetic;
const uint32_t mSize; /* size in original file */
const uint32_t mRealsize; /* inflated size */
const uint32_t mCrc32;
const PRTime mLastModTime;
const uint16_t mCompression;
const uint32_t mPermissions;
const bool mIsDirectory;
const bool mIsSynthetic;
};
/**
@ -150,6 +150,8 @@ class nsJAREnumerator final : public nsStringEnumeratorBase {
class nsZipReaderCache : public nsIZipReaderCache,
public nsIObserver,
public nsSupportsWeakReference {
friend class nsJAR;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIZIPREADERCACHE
@ -162,6 +164,8 @@ class nsZipReaderCache : public nsIZipReaderCache,
typedef nsRefPtrHashtable<nsCStringHashKey, nsJAR> ZipsHashtable;
protected:
void AssertLockOwned() { mLock.AssertCurrentThreadOwns(); }
virtual ~nsZipReaderCache();
mozilla::Mutex mLock MOZ_UNANNOTATED;

View File

@ -25,17 +25,19 @@ NS_IMPL_ISUPPORTS(nsJARInputStream, nsIInputStream)
/*----------------------------------------------------------
* nsJARInputStream implementation
* Takes ownership of |fd|, even on failure
*--------------------------------------------------------*/
nsresult nsJARInputStream::InitFile(nsJAR* aJar, nsZipItem* item) {
nsresult nsJARInputStream::InitFile(nsZipHandle* aFd, const uint8_t* aData,
nsZipItem* aItem) {
nsresult rv = NS_OK;
MOZ_ASSERT(aJar, "Argument may not be null");
MOZ_ASSERT(item, "Argument may not be null");
MOZ_ASSERT(aFd, "Argument may not be null");
MOZ_ASSERT(aItem, "Argument may not be null");
// Mark it as closed, in case something fails in initialisation
mMode = MODE_CLOSED;
//-- prepare for the compression type
switch (item->Compression()) {
switch (aItem->Compression()) {
case STORED:
mMode = MODE_COPY;
break;
@ -45,23 +47,24 @@ nsresult nsJARInputStream::InitFile(nsJAR* aJar, nsZipItem* item) {
NS_ENSURE_SUCCESS(rv, rv);
mMode = MODE_INFLATE;
mInCrc = item->CRC32();
mInCrc = aItem->CRC32();
mOutCrc = crc32(0L, Z_NULL, 0);
break;
default:
mFd = aFd;
return NS_ERROR_NOT_IMPLEMENTED;
}
// Must keep handle to filepointer and mmap structure as long as we need
// access to the mmapped data
mFd = aJar->mZip->GetFD();
mZs.next_in = (Bytef*)aJar->mZip->GetData(item);
mFd = aFd;
mZs.next_in = (Bytef*)aData;
if (!mZs.next_in) {
return NS_ERROR_FILE_CORRUPTED;
}
mZs.avail_in = item->Size();
mOutSize = item->RealSize();
mZs.avail_in = aItem->Size();
mOutSize = aItem->RealSize();
mZs.total_out = 0;
return NS_OK;
}
@ -77,7 +80,8 @@ nsresult nsJARInputStream::InitDirectory(nsJAR* aJar,
// Keep the zipReader for getting the actual zipItems
mJar = aJar;
nsZipFind* find;
mJar->mLock.AssertCurrentThreadIn();
UniquePtr<nsZipFind> find;
nsresult rv;
// We can get aDir's contents as strings via FindEntries
// with the following pattern (see nsIZipReader.findEntries docs)
@ -113,7 +117,7 @@ nsresult nsJARInputStream::InitDirectory(nsJAR* aJar,
++curr;
}
nsAutoCString pattern = escDirName + "?*~"_ns + escDirName + "?*/?*"_ns;
rv = mJar->mZip->FindInit(pattern.get(), &find);
rv = mJar->mZip->FindInit(pattern.get(), getter_Transfers(find));
if (NS_FAILED(rv)) return rv;
const char* name;
@ -122,7 +126,6 @@ nsresult nsJARInputStream::InitDirectory(nsJAR* aJar,
// Must copy, to make it zero-terminated
mArray.AppendElement(nsCString(name, nameLen));
}
delete find;
if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && NS_FAILED(rv)) {
return NS_ERROR_FAILURE; // no error translation
@ -301,6 +304,7 @@ nsresult nsJARInputStream::ReadDirectory(char* aBuffer, uint32_t aCount,
uint32_t numRead = CopyDataToBuffer(aBuffer, aCount);
if (aCount > 0) {
RecursiveMutexAutoLock lock(mJar->mLock);
// empty the buffer and start writing directory entry lines to it
mBuffer.Truncate();
mCurPos = 0;

View File

@ -34,7 +34,7 @@ class nsJARInputStream final : public nsIInputStream {
NS_DECL_NSIINPUTSTREAM
// takes ownership of |fd|, even on failure
nsresult InitFile(nsJAR* aJar, nsZipItem* item);
nsresult InitFile(nsZipHandle* aFd, const uint8_t* aData, nsZipItem* item);
nsresult InitDirectory(nsJAR* aJar, const nsACString& aJarDirSpec,
const char* aDir);

View File

@ -5,9 +5,6 @@
/*
* 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.
*/
#define READTYPE int32_t
@ -337,65 +334,20 @@ nsZipHandle::~nsZipHandle() {
//---------------------------------------------
// nsZipArchive::OpenArchive
//---------------------------------------------
nsresult nsZipArchive::OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd) {
mFd = aZipHandle;
//-- get table of contents for archive
nsresult rv = BuildFileList(aFd);
if (NS_SUCCEEDED(rv)) {
if (aZipHandle->mFile && XRE_IsParentProcess()) {
static char* env = PR_GetEnv("MOZ_JAR_LOG_FILE");
if (env) {
mUseZipLog = true;
zipLog.Init(env);
// We only log accesses in jar/zip archives within the NS_GRE_DIR
// and/or the APK on Android. For the former, we log the archive path
// relative to NS_GRE_DIR, and for the latter, the nested-archive
// path within the APK. This makes the path match the path of the
// archives relative to the packaged dist/$APP_NAME directory in a
// build.
if (aZipHandle->mFile.IsZip()) {
// Nested archive, likely omni.ja in APK.
aZipHandle->mFile.GetPath(mURI);
} else if (nsDirectoryService::gService) {
// We can reach here through the initialization of Omnijar from
// XRE_InitCommandLine, which happens before the directory service
// is initialized. When that happens, it means the opened archive is
// the APK, and we don't care to log that one, so we just skip
// when the directory service is not initialized.
nsCOMPtr<nsIFile> dir = aZipHandle->mFile.GetBaseFile();
nsCOMPtr<nsIFile> gre_dir;
nsAutoCString path;
if (NS_SUCCEEDED(nsDirectoryService::gService->Get(
NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(gre_dir)))) {
nsAutoCString leaf;
nsCOMPtr<nsIFile> parent;
while (NS_SUCCEEDED(dir->GetNativeLeafName(leaf)) &&
NS_SUCCEEDED(dir->GetParent(getter_AddRefs(parent)))) {
if (!parent) {
break;
}
dir = parent;
if (path.Length()) {
path.Insert('/', 0);
}
path.Insert(leaf, 0);
bool equals;
if (NS_SUCCEEDED(dir->Equals(gre_dir, &equals)) && equals) {
mURI.Assign(path);
break;
}
}
}
}
}
}
/* static */
already_AddRefed<nsZipArchive> nsZipArchive::OpenArchive(
nsZipHandle* aZipHandle, PRFileDesc* aFd) {
nsresult rv;
RefPtr<nsZipArchive> self(new nsZipArchive(aZipHandle, aFd, rv));
LOG(("ZipHandle::OpenArchive[%p]", self.get()));
if (NS_FAILED(rv)) {
self = nullptr;
}
return rv;
return self.forget();
}
nsresult nsZipArchive::OpenArchive(nsIFile* aFile) {
/* static */
already_AddRefed<nsZipArchive> nsZipArchive::OpenArchive(nsIFile* aFile) {
RefPtr<nsZipHandle> handle;
#if defined(XP_WIN)
mozilla::AutoFDClose fd;
@ -403,7 +355,7 @@ nsresult nsZipArchive::OpenArchive(nsIFile* aFile) {
#else
nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
#endif
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv)) return nullptr;
#if defined(XP_WIN)
return OpenArchive(handle, fd.get());
@ -440,28 +392,6 @@ nsresult nsZipArchive::Test(const char* aEntryName) {
return NS_OK;
}
//---------------------------------------------
// nsZipArchive::CloseArchive
//---------------------------------------------
nsresult nsZipArchive::CloseArchive() {
MutexAutoLock lock(mLock);
if (mFd) {
mArena.Clear();
mFd = nullptr;
}
// CAUTION:
// 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
memset(mFiles, 0, sizeof(mFiles));
mBuiltSynthetics = false;
return NS_OK;
}
//---------------------------------------------
// nsZipArchive::GetItem
//---------------------------------------------
@ -504,6 +434,7 @@ nsZipItem* nsZipArchive::GetItem(const char* aEntryName) {
//---------------------------------------------
nsresult nsZipArchive::ExtractFile(nsZipItem* item, nsIFile* outFile,
PRFileDesc* aFd) {
MutexAutoLock lock(mLock);
if (!item) return NS_ERROR_ILLEGAL_VALUE;
if (!mFd) return NS_ERROR_FAILURE;
@ -651,7 +582,8 @@ nsZipItem* nsZipArchive::CreateZipItem() {
// nsZipArchive::BuildFileList
//---------------------------------------------
nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) {
MutexAutoLock lock(mLock);
// We're only called from the constructor, but need to call
// CreateZipItem(), which touches locked data, and modify mFiles.
// Get archive size using end pos
const uint8_t* buf;
@ -738,18 +670,6 @@ nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) {
return NS_ERROR_FILE_CORRUPTED;
}
// Make the comment available for consumers.
if ((endp >= buf) && (endp - buf >= ZIPEND_SIZE)) {
ZipEnd* zipend = (ZipEnd*)buf;
buf += ZIPEND_SIZE;
uint16_t commentlen = xtoint(zipend->commentfield_len);
if (endp - buf >= commentlen) {
mCommentPtr = (const char*)buf;
mCommentLen = commentlen;
}
}
MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)
return NS_OK;
}
@ -821,10 +741,10 @@ nsresult nsZipArchive::BuildSynthetics() {
return NS_OK;
}
nsZipHandle* nsZipArchive::GetFD() {
if (!mFd) return nullptr;
return mFd.get();
}
//---------------------------------------------
// nsZipArchive::GetFD
//---------------------------------------------
nsZipHandle* nsZipArchive::GetFD() const { return mFd.get(); }
//---------------------------------------------
// nsZipArchive::GetDataOffset
@ -875,14 +795,6 @@ const uint8_t* nsZipArchive::GetData(nsZipItem* aItem) {
return mFd->mFileData + offset;
}
// nsZipArchive::GetComment
bool nsZipArchive::GetComment(nsACString& aComment) {
MMAP_FAULT_HANDLER_BEGIN_BUFFER(mCommentPtr, mCommentLen)
aComment.Assign(mCommentPtr, mCommentLen);
MMAP_FAULT_HANDLER_CATCH(false)
return true;
}
//---------------------------------------------
// nsZipArchive::SizeOfMapping
//---------------------------------------------
@ -892,22 +804,71 @@ int64_t nsZipArchive::SizeOfMapping() { return mFd ? mFd->SizeOfMapping() : 0; }
// nsZipArchive constructor and destructor
//------------------------------------------
nsZipArchive::nsZipArchive()
: mRefCnt(0),
mCommentPtr(nullptr),
mCommentLen(0),
mBuiltSynthetics(false),
mUseZipLog(false) {
nsZipArchive::nsZipArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd,
nsresult& aRv)
: mRefCnt(0), mFd(aZipHandle), mUseZipLog(false), mBuiltSynthetics(false) {
// initialize the table to nullptr
memset(mFiles, 0, sizeof(mFiles));
//-- get table of contents for archive
aRv = BuildFileList(aFd);
if (NS_FAILED(aRv)) {
return; // whomever created us must destroy us in this case
}
if (aZipHandle->mFile && XRE_IsParentProcess()) {
static char* env = PR_GetEnv("MOZ_JAR_LOG_FILE");
if (env) {
mUseZipLog = true;
zipLog.Init(env);
// We only log accesses in jar/zip archives within the NS_GRE_DIR
// and/or the APK on Android. For the former, we log the archive path
// relative to NS_GRE_DIR, and for the latter, the nested-archive
// path within the APK. This makes the path match the path of the
// archives relative to the packaged dist/$APP_NAME directory in a
// build.
if (aZipHandle->mFile.IsZip()) {
// Nested archive, likely omni.ja in APK.
aZipHandle->mFile.GetPath(mURI);
} else if (nsDirectoryService::gService) {
// We can reach here through the initialization of Omnijar from
// XRE_InitCommandLine, which happens before the directory service
// is initialized. When that happens, it means the opened archive is
// the APK, and we don't care to log that one, so we just skip
// when the directory service is not initialized.
nsCOMPtr<nsIFile> dir = aZipHandle->mFile.GetBaseFile();
nsCOMPtr<nsIFile> gre_dir;
nsAutoCString path;
if (NS_SUCCEEDED(nsDirectoryService::gService->Get(
NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(gre_dir)))) {
nsAutoCString leaf;
nsCOMPtr<nsIFile> parent;
while (NS_SUCCEEDED(dir->GetNativeLeafName(leaf)) &&
NS_SUCCEEDED(dir->GetParent(getter_AddRefs(parent)))) {
if (!parent) {
break;
}
dir = parent;
if (path.Length()) {
path.Insert('/', 0);
}
path.Insert(leaf, 0);
bool equals;
if (NS_SUCCEEDED(dir->Equals(gre_dir, &equals)) && equals) {
mURI.Assign(path);
break;
}
}
}
}
}
}
}
NS_IMPL_ADDREF(nsZipArchive)
NS_IMPL_RELEASE(nsZipArchive)
nsZipArchive::~nsZipArchive() {
CloseArchive();
if (mUseZipLog) {
zipLog.Release();
}

View File

@ -38,6 +38,8 @@ struct PRFileDesc;
* 'MT''safe' reading from the zipfile is performed through JARInputStream,
* which maintains its own file descriptor, allowing for multiple reads
* concurrently from the same zip file.
*
* nsZipArchives are accessed from multiple threads.
*/
/**
@ -85,21 +87,15 @@ class nsZipArchive final {
public:
static const char* sFileCorruptedReason;
/** constructing does not open the archive. See OpenArchive() */
nsZipArchive();
/**
* OpenArchive
*
* It's an error to call this more than once on the same nsZipArchive
* object. If we were allowed to use exceptions this would have been
* part of the constructor
*
* @param aZipHandle The nsZipHandle used to access the zip
* @param aFd Optional PRFileDesc for Windows readahead optimization
* @return status code
*/
nsresult OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd = nullptr);
static already_AddRefed<nsZipArchive> OpenArchive(nsZipHandle* aZipHandle,
PRFileDesc* aFd = nullptr);
/**
* OpenArchive
@ -109,7 +105,7 @@ class nsZipArchive final {
* @param aFile The file used to access the zip
* @return status code
*/
nsresult OpenArchive(nsIFile* aFile);
static already_AddRefed<nsZipArchive> OpenArchive(nsIFile* aFile);
/**
* Test the integrity of items in this archive by running
@ -122,11 +118,6 @@ class nsZipArchive final {
*/
nsresult Test(const char* aEntryName);
/**
* Closes an open archive.
*/
nsresult CloseArchive();
/**
* GetItem
* @param aEntryName Name of file in the archive
@ -163,7 +154,7 @@ class nsZipArchive final {
/*
* Gets an undependent handle to the mapped file.
*/
nsZipHandle* GetFD();
nsZipHandle* GetFD() const;
/**
* Gets the data offset.
@ -179,8 +170,6 @@ class nsZipArchive final {
*/
const uint8_t* GetData(nsZipItem* aItem);
bool GetComment(nsACString& aComment);
/**
* Gets the amount of memory taken up by the archive's mapping.
* @return the size
@ -194,30 +183,28 @@ class nsZipArchive final {
NS_METHOD_(MozExternalRefCountType) Release(void);
private:
nsZipArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd, nsresult& aRv);
//--- private members ---
mozilla::ThreadSafeAutoRefCnt mRefCnt; /* ref count */
NS_DECL_OWNINGTHREAD
nsZipItem* mFiles[ZIP_TABSIZE];
mozilla::ArenaAllocator<1024, sizeof(void*)> mArena;
const char* mCommentPtr;
uint16_t mCommentLen;
// Whether we synthesized the directory entries
bool mBuiltSynthetics;
// These fields are all effectively const after the constructor
// file handle
RefPtr<nsZipHandle> mFd;
const RefPtr<nsZipHandle> mFd;
// file URI, for logging
nsCString mURI;
// Is true if we use zipLog to log accesses in jar/zip archives. This helper
// variable avoids grabbing zipLog's lock when not necessary.
// Effectively const after constructor
bool mUseZipLog;
mozilla::Mutex mLock{"nsZipArchive"};
// all of the following members are guarded by mLock:
nsZipItem* mFiles[ZIP_TABSIZE];
mozilla::ArenaAllocator<1024, sizeof(void*)> mArena;
// Whether we synthesized the directory entries
bool mBuiltSynthetics;
private:
//--- private methods ---

View File

@ -15,6 +15,10 @@ function run_test() {
);
zipreader.open(file);
zipreader.close();
var entries = zipreader.findEntries("*.*");
Assert.ok(!entries.hasMore()); // this shouldn't crash
// this should error out and not crash
Assert.throws(
() => zipreader.findEntries("*.*"),
/NS_ERROR_FAILURE/,
"Should error out on a closed zipreader"
);
}

View File

@ -310,6 +310,12 @@ var AddonTestUtils = {
}
testScope.registerCleanupFunction(() => {
// Force a GC to ensure that anything holding a ref to temp file releases it.
// XXX This shouldn't be needed here, since cleanupTempXPIs() does a GC if
// something fails; see bug 1761255
this.info(`Force a GC`);
Cu.forceGC();
this.cleanupTempXPIs();
let ignoreEntries = new Set();

View File

@ -151,8 +151,7 @@ nsresult FileLocation::GetData(Data& aData) {
}
aData.mZip = mBaseZip;
if (!aData.mZip) {
aData.mZip = new nsZipArchive();
aData.mZip->OpenArchive(mBaseFile);
aData.mZip = nsZipArchive::OpenArchive(mBaseFile);
}
aData.mItem = aData.mZip->GetItem(mPath.get());
if (aData.mItem) {

View File

@ -26,11 +26,9 @@ static const char* sProp[2] = {NS_GRE_DIR, NS_XPCOM_CURRENT_PROCESS_DIR};
void Omnijar::CleanUpOne(Type aType) {
if (sReader[aType]) {
sReader[aType]->CloseArchive();
sReader[aType] = nullptr;
}
if (sOuterReader[aType]) {
sOuterReader[aType]->CloseArchive();
sOuterReader[aType] = nullptr;
}
sPath[aType] = nullptr;
@ -77,8 +75,8 @@ void Omnijar::InitOne(nsIFile* aPath, Type aType) {
return;
}
RefPtr<nsZipArchive> zipReader = new nsZipArchive();
if (NS_FAILED(zipReader->OpenArchive(file))) {
RefPtr<nsZipArchive> zipReader = nsZipArchive::OpenArchive(file);
if (!zipReader) {
return;
}
@ -87,8 +85,8 @@ void Omnijar::InitOne(nsIFile* aPath, Type aType) {
if (NS_SUCCEEDED(nsZipHandle::Init(zipReader, MOZ_STRINGIFY(OMNIJAR_NAME),
getter_AddRefs(handle)))) {
outerReader = zipReader;
zipReader = new nsZipArchive();
if (NS_FAILED(zipReader->OpenArchive(handle))) {
zipReader = nsZipArchive::OpenArchive(handle);
if (!zipReader) {
return;
}
}