зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1627075 - Allow lazily initializing nsZipArchives r=froydnj
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
This commit is contained in:
Родитель
fafe31f134
Коммит
2046625fac
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -562,6 +625,12 @@ 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;
|
||||
|
||||
|
@ -569,7 +638,7 @@ nsresult nsZipArchive::FindInit(const char* aPattern, nsZipFind** aFind) {
|
|||
char* pattern = 0;
|
||||
|
||||
// Create synthetic directory entries on demand
|
||||
nsresult rv = BuildSynthetics();
|
||||
rv = BuildSynthetics();
|
||||
if (rv != NS_OK) return rv;
|
||||
|
||||
// validate the pattern
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
#include "nsIFile.h"
|
||||
#include "nsISupportsImpl.h" // For mozilla::ThreadSafeAutoRefCnt
|
||||
#include "mozilla/ArenaAllocator.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/FileLocation.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/RWLock.h"
|
||||
|
||||
class nsZipFind;
|
||||
struct PRFileDesc;
|
||||
|
@ -84,6 +86,22 @@ class nsZipArchive final {
|
|||
/** destructing the object closes the archive */
|
||||
~nsZipArchive();
|
||||
|
||||
/**
|
||||
* LazyOpenArchiveParams is a class which is used to store cached
|
||||
* contents of omnijars.
|
||||
*
|
||||
*/
|
||||
struct LazyOpenArchiveParams {
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
mozilla::Span<const uint8_t> mCachedCentral;
|
||||
|
||||
LazyOpenArchiveParams(nsIFile* aFile,
|
||||
mozilla::Span<const uint8_t> aCachedCentral)
|
||||
: mFile(nullptr), mCachedCentral(aCachedCentral) {
|
||||
aFile->Clone(getter_AddRefs(mFile));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static const char* sFileCorruptedReason;
|
||||
|
||||
|
@ -102,12 +120,11 @@ class nsZipArchive final {
|
|||
optimization
|
||||
* @param aCachedCentral Optional cached buffer containing the zip central
|
||||
for this zip.
|
||||
* @param aCachedCentralSize Optional size of aCachedCentral.
|
||||
* @return status code
|
||||
*/
|
||||
nsresult OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd = nullptr,
|
||||
const uint8_t* aCachedCentral = nullptr,
|
||||
size_t aCachedCentralSize = 0);
|
||||
mozilla::Span<const uint8_t> aCachedCentral =
|
||||
mozilla::Span<const uint8_t>());
|
||||
|
||||
/**
|
||||
* OpenArchive
|
||||
|
@ -117,11 +134,44 @@ class nsZipArchive final {
|
|||
* @param aFile The file used to access the zip
|
||||
* @param aCachedCentral Optional cached buffer containing the zip central
|
||||
for this zip.
|
||||
* @param aCachedCentralSize Optional size of aCachedCentral.
|
||||
* @return status code
|
||||
*/
|
||||
nsresult OpenArchive(nsIFile* aFile, const uint8_t* aCachedCentral = nullptr,
|
||||
size_t aCachedCentralSize = 0);
|
||||
nsresult OpenArchive(nsIFile* aFile,
|
||||
mozilla::Span<const uint8_t> aCachedCentral =
|
||||
mozilla::Span<const uint8_t>());
|
||||
|
||||
/**
|
||||
* Ensures underlying archive is opened, if it was opened with
|
||||
* LazyOpenArchive.
|
||||
*
|
||||
* Convenience function that generates nsZipHandle
|
||||
*
|
||||
* @param aFile The file used to access the zip
|
||||
* @return status code
|
||||
*/
|
||||
nsresult EnsureArchiveOpenedOnDisk();
|
||||
|
||||
/**
|
||||
* OpenArchive
|
||||
*
|
||||
* Lazily opens the zip archive on the first request to get data from it.
|
||||
* NOTE: The buffer provided for aCachedCentral must outlive this
|
||||
* nsZipArchive. This is presently true for the StartupCache, as it ensures
|
||||
* that even past cache invalidation, all accessed buffers persist for the
|
||||
* lifetime of the application, but we will need to ensure that this remains
|
||||
* true.
|
||||
*
|
||||
* @param aFile The file used to access the zip
|
||||
* @param aCachedCentral Cached buffer containing the zip central
|
||||
for this zip.
|
||||
* @return status code
|
||||
*/
|
||||
nsresult LazyOpenArchive(nsIFile* aFile,
|
||||
mozilla::Span<const uint8_t> aCachedCentral) {
|
||||
mozilla::AutoWriteLock lock(mLazyOpenLock);
|
||||
mLazyOpenParams.emplace(aFile, aCachedCentral);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the integrity of items in this archive by running
|
||||
|
@ -177,6 +227,21 @@ class nsZipArchive final {
|
|||
*/
|
||||
nsZipHandle* GetFD();
|
||||
|
||||
/*
|
||||
* Gets the URI string to the mapped file. One could get this URI string
|
||||
* in a roundabout way using GetFD, but GetFD requires opening the file for
|
||||
* read access, which can be expensive.
|
||||
*/
|
||||
void GetURIString(nsACString& result);
|
||||
|
||||
/*
|
||||
* Gets the underlying nsIFile pointer. Like GetURIString, this is to be
|
||||
* preferred over GetFD where possible, because it does not require opening
|
||||
* the file for read access, which can be expensive, and is to be avoided
|
||||
* when possible during application startup.
|
||||
*/
|
||||
already_AddRefed<nsIFile> GetBaseFile();
|
||||
|
||||
/**
|
||||
* Gets the data offset.
|
||||
* @param aItem Pointer to nsZipItem
|
||||
|
@ -231,10 +296,14 @@ class nsZipArchive final {
|
|||
|
||||
// Whether we synthesized the directory entries
|
||||
bool mBuiltSynthetics;
|
||||
bool mBuiltFileList;
|
||||
|
||||
// file handle
|
||||
RefPtr<nsZipHandle> mFd;
|
||||
|
||||
mozilla::Maybe<LazyOpenArchiveParams> mLazyOpenParams;
|
||||
mozilla::RWLock mLazyOpenLock;
|
||||
|
||||
// file URI, for logging
|
||||
nsCString mURI;
|
||||
|
||||
|
@ -248,6 +317,7 @@ class nsZipArchive final {
|
|||
nsresult BuildFileList(PRFileDesc* aFd = nullptr);
|
||||
nsresult BuildFileListFromBuffer(const uint8_t* aBuf, const uint8_t* aEnd);
|
||||
nsresult BuildSynthetics();
|
||||
nsresult EnsureFileListBuilt();
|
||||
|
||||
nsZipArchive& operator=(const nsZipArchive& rhs) = delete;
|
||||
nsZipArchive(const nsZipArchive& rhs) = delete;
|
||||
|
|
|
@ -93,20 +93,30 @@ void Omnijar::InitOne(nsIFile* aPath, Type aType) {
|
|||
centralBufLength = 0;
|
||||
}
|
||||
}
|
||||
if (NS_FAILED(zipReader->OpenArchive(file, centralBuf, centralBufLength))) {
|
||||
if (NS_FAILED(zipReader->OpenArchive(
|
||||
file, MakeSpan(centralBuf, centralBufLength)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cache && !centralBuf) {
|
||||
size_t bufSize;
|
||||
|
||||
// Annoyingly, nsZipArchive and the startupcache use different types to
|
||||
// represent bytes (uint8_t vs char), so we have to do a little dance to
|
||||
// convert the UniquePtr over.
|
||||
UniquePtr<char[]> centralBuf(reinterpret_cast<char*>(
|
||||
zipReader->CopyCentralDirectoryBuffer(&bufSize).release()));
|
||||
if (centralBuf) {
|
||||
cache->PutBuffer(startupCacheKey.get(), std::move(centralBuf), bufSize);
|
||||
if (!centralBuf) {
|
||||
if (NS_FAILED(zipReader->OpenArchive(file))) {
|
||||
return;
|
||||
}
|
||||
if (cache) {
|
||||
size_t bufSize;
|
||||
// Annoyingly, nsZipArchive and the startupcache use different types to
|
||||
// represent bytes (uint8_t vs char), so we have to do a little dance to
|
||||
// convert the UniquePtr over.
|
||||
UniquePtr<char[]> centralBuf(reinterpret_cast<char*>(
|
||||
zipReader->CopyCentralDirectoryBuffer(&bufSize).release()));
|
||||
if (centralBuf) {
|
||||
cache->PutBuffer(startupCacheKey.get(), std::move(centralBuf), bufSize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (NS_FAILED(zipReader->LazyOpenArchive(
|
||||
file, MakeSpan(centralBuf, centralBufLength)))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче