Skip to content

Commit

Permalink
Bug 1627075 - Allow lazily initializing nsZipArchives r=froydnj
Browse files Browse the repository at this point in the history
Opening our Omnijars can be expensive, and it should be deferrable until after
startup is completed, provided we have a startup cache. In a previous patch in this
stack, we implemented caching of the zip central directory for omnijars, but we
still have to open the file in order to hand the object off to various omnijar
consumers. In a later patch, we will wrap nsZipArchive access in a class which
will allow us to transparently cache nsZipArchive results. These two get us
most of the way to not needing to read from the underlying omnijar files during
startup, but there are still nontrivial pieces, like nsZipFind for instance,
which we don't want to just duplicate inside of a wrapper class, so we would
like to sort out a way in which we can use an nsZipArchive class, but not
actually back it up with the real underlying file until we really need data
from it which we can't find in a cache.

Depends on D77633

Differential Revision: https://phabricator.services.mozilla.com/D78584
  • Loading branch information
squarewave committed Jul 7, 2020
1 parent fafe31f commit 2046625
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 36 deletions.
171 changes: 152 additions & 19 deletions modules/libjar/nsZipArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,17 +342,19 @@ nsZipHandle::~nsZipHandle() {
// nsZipArchive::OpenArchive
//---------------------------------------------
nsresult nsZipArchive::OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd,
const uint8_t* aCachedCentral,
size_t aCachedCentralSize) {
Span<const uint8_t> aCachedCentral) {
mFd = aZipHandle;

//-- get table of contents for archive
nsresult rv;
if (aCachedCentral) {
rv = BuildFileListFromBuffer(aCachedCentral,
aCachedCentral + aCachedCentralSize);
} else {
rv = BuildFileList(aFd);
nsresult rv = NS_OK;
if (!mBuiltFileList) {
if (!aCachedCentral.IsEmpty()) {
auto* start = aCachedCentral.Elements();
auto* end = start + aCachedCentral.Length();
rv = BuildFileListFromBuffer(start, end);
} else {
rv = BuildFileList(aFd);
}
}
if (NS_SUCCEEDED(rv)) {
if (aZipHandle->mFile && XRE_IsParentProcess()) {
Expand Down Expand Up @@ -408,8 +410,7 @@ nsresult nsZipArchive::OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd,
}

nsresult nsZipArchive::OpenArchive(nsIFile* aFile,
const uint8_t* aCachedCentral,
size_t aCachedCentralSize) {
Span<const uint8_t> aCachedCentral) {
RefPtr<nsZipHandle> handle;
#if defined(XP_WIN)
mozilla::AutoFDClose fd;
Expand All @@ -420,9 +421,9 @@ nsresult nsZipArchive::OpenArchive(nsIFile* aFile,
if (NS_FAILED(rv)) return rv;

#if defined(XP_WIN)
return OpenArchive(handle, fd.get(), aCachedCentral, aCachedCentralSize);
return OpenArchive(handle, fd.get(), aCachedCentral);
#else
return OpenArchive(handle, nullptr, aCachedCentral, aCachedCentralSize);
return OpenArchive(handle, nullptr, aCachedCentral);
#endif
}

Expand Down Expand Up @@ -472,13 +473,75 @@ nsresult nsZipArchive::CloseArchive() {
// Let us also cleanup the mFiles table for re-use on the next 'open' call
memset(mFiles, 0, sizeof(mFiles));
mBuiltSynthetics = false;

AutoWriteLock lock(mLazyOpenLock);
mLazyOpenParams = Nothing();

return NS_OK;
}

nsresult nsZipArchive::EnsureArchiveOpenedOnDisk() {
{
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
return NS_OK;
}
}

AutoWriteLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
// Another thread beat us to opening the archive while we were waiting on
// the mutex.
return NS_OK;
}

nsresult rv = OpenArchive(mLazyOpenParams->mFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
mLazyOpenParams = Nothing();

return NS_OK;
}

nsresult nsZipArchive::EnsureFileListBuilt() {
{
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams || mBuiltFileList) {
return NS_OK;
}
}

AutoWriteLock lock(mLazyOpenLock);
if (!mLazyOpenParams || mBuiltFileList) {
// Another thread beat us to building the file list while we were waiting
// on the mutex.
return NS_OK;
}

nsresult rv;
if (!mLazyOpenParams->mCachedCentral.IsEmpty()) {
auto* start = mLazyOpenParams->mCachedCentral.Elements();
auto* end = start + mLazyOpenParams->mCachedCentral.Length();
rv = BuildFileListFromBuffer(start, end);
} else {
rv = OpenArchive(mLazyOpenParams->mFile);
mLazyOpenParams = Nothing();
}

return rv;
}

//---------------------------------------------
// nsZipArchive::GetItem
//---------------------------------------------
nsZipItem* nsZipArchive::GetItem(const char* aEntryName) {
nsresult rv = EnsureFileListBuilt();

if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}

if (aEntryName) {
uint32_t len = strlen(aEntryName);
//-- If the request is for a directory, make sure that synthetic entries
Expand Down Expand Up @@ -562,14 +625,20 @@ nsresult nsZipArchive::ExtractFile(nsZipItem* item, nsIFile* outFile,
nsresult nsZipArchive::FindInit(const char* aPattern, nsZipFind** aFind) {
if (!aFind) return NS_ERROR_ILLEGAL_VALUE;

nsresult rv = EnsureFileListBuilt();

if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}

// null out param in case an error happens
*aFind = nullptr;

bool regExp = false;
char* pattern = 0;

// Create synthetic directory entries on demand
nsresult rv = BuildSynthetics();
rv = BuildSynthetics();
if (rv != NS_OK) return rv;

// validate the pattern
Expand Down Expand Up @@ -613,7 +682,12 @@ nsresult nsZipFind::FindNext(const char** aResult, uint16_t* aNameLen) {

*aResult = 0;
*aNameLen = 0;
MMAP_FAULT_HANDLER_BEGIN_HANDLE(mArchive->GetFD())

// NOTE: don't use GetFD here. if mFd is not null, then we need to have this
// fault handler, as we may be reading from the memory mapped file. However
// if it is null, then we can guarantee that we're reading here from a cached
// buffer, which we assume is not mapped to a file.
MMAP_FAULT_HANDLER_BEGIN_HANDLE(mArchive->mFd)
// we start from last match, look for next
while (mSlot < ZIP_TABSIZE) {
// move to next in current chain, or move to new slot
Expand Down Expand Up @@ -659,6 +733,7 @@ nsZipItem* nsZipArchive::CreateZipItem() {
// nsZipArchive::BuildFileList
//---------------------------------------------
nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) {
mBuiltFileList = true;
// Get archive size using end pos
const uint8_t* buf;
const uint8_t* startp = mFd->mFileData;
Expand Down Expand Up @@ -689,7 +764,7 @@ nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) {
return NS_ERROR_FILE_CORRUPTED;
}

uintptr_t startpInt = (uintptr_t)startp;
uintptr_t startpInt = reinterpret_cast<uintptr_t>(startp);
if (startpInt + centralOffset < startpInt || centralOffset > mFd->mLen) {
return NS_ERROR_FILE_CORRUPTED;
}
Expand Down Expand Up @@ -737,6 +812,7 @@ UniquePtr<uint8_t[]> nsZipArchive::CopyCentralDirectoryBuffer(size_t* aSize) {

nsresult nsZipArchive::BuildFileListFromBuffer(const uint8_t* aBuf,
const uint8_t* aEnd) {
mBuiltFileList = true;
const uint8_t* buf = aBuf;
//-- Read the central directory headers
uint32_t sig = 0;
Expand Down Expand Up @@ -870,15 +946,64 @@ nsresult nsZipArchive::BuildSynthetics() {
}

nsZipHandle* nsZipArchive::GetFD() {
if (!mFd) return nullptr;
nsresult rv = EnsureArchiveOpenedOnDisk();
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return mFd.get();
}

void nsZipArchive::GetURIString(nsACString& result) {
{
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
mFd->mFile.GetURIString(result);
return;
}
}
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
// Another thread consumed mLazyOpenParams while we were waiting.
mFd->mFile.GetURIString(result);
return;
}

// This is a bit tricky - typically, we could just
// NS_GetURLSpecFromActualFile from mLazyOpenParams->mFile or from
// mFd->mFile.GetBaseFile(), depending on which we currently have. However,
// this won't actually be correct if this zip archive is nested inside
// another archive. However, at present, we know that mLazyOpenParams can
// only be here if we were opened from a real underlying file, so we assume
// that we're safe to do this. Any future code that breaks this assumption
// will need to update things here.
NS_GetURLSpecFromActualFile(mLazyOpenParams->mFile, result);
}

already_AddRefed<nsIFile> nsZipArchive::GetBaseFile() {
{
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
return mFd->mFile.GetBaseFile();
}
}
AutoReadLock lock(mLazyOpenLock);
if (!mLazyOpenParams) {
// Another thread consumed mLazyOpenParams while we were waiting.
return mFd->mFile.GetBaseFile();
}

nsCOMPtr<nsIFile> file = mLazyOpenParams->mFile;
return file.forget();
}

//---------------------------------------------
// nsZipArchive::GetDataOffset
//---------------------------------------------
uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem) {
MOZ_ASSERT(aItem);
nsresult rv = EnsureArchiveOpenedOnDisk();
MOZ_RELEASE_ASSERT(!NS_FAILED(rv),
"Should have been able to open the zip archive");

uint32_t offset;
MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
Expand Down Expand Up @@ -909,6 +1034,10 @@ uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem) {
//---------------------------------------------
const uint8_t* nsZipArchive::GetData(nsZipItem* aItem) {
MOZ_ASSERT(aItem);
nsresult rv = EnsureArchiveOpenedOnDisk();
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
uint32_t offset = GetDataOffset(aItem);

MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
Expand Down Expand Up @@ -947,6 +1076,8 @@ nsZipArchive::nsZipArchive()
mZipCentralSize(0),
mCommentLen(0),
mBuiltSynthetics(false),
mBuiltFileList(false),
mLazyOpenLock("nsZipArchive::mLazyOpenLock"),
mUseZipLog(false) {
// initialize the table to nullptr
memset(mFiles, 0, sizeof(mFiles));
Expand Down Expand Up @@ -1254,12 +1385,14 @@ uint8_t* nsZipCursor::ReadOrCopy(uint32_t* aBytesRead, bool aCopy) {
nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive* aZip, const char* aEntryName,
bool doCRC)
: mReturnBuf(nullptr), mReadlen(0) {
nsZipItem* item = aZip->GetItem(aEntryName);
if (!item) {
return;
}

// make sure the ziparchive hangs around
mZipHandle = aZip->GetFD();

nsZipItem* item = aZip->GetItem(aEntryName);
if (!item) return;

uint32_t size = 0;
bool compressed = (item->Compression() == DEFLATED);
#ifdef MOZ_JAR_BROTLI
Expand Down
Loading

0 comments on commit 2046625

Please sign in to comment.