/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <string.h> #include"nsJARInputStream.h" #include"nsJAR.h" #include"nsIFile.h" #include"nsIObserverService.h" #include"mozilla/DebugOnly.h" #include"mozilla/Logging.h" #include"mozilla/Omnijar.h" #include"mozilla/Unused.h"
#ifdef XP_UNIX # include <sys/stat.h> #elifdefined(XP_WIN) # include <io.h> #endif
// The following initialization makes a guess of 10 entries per jarfile.
nsJAR::nsJAR()
: mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
mLock("nsJAR::mLock"),
mCache(nullptr) {}
// Custom Release method works with nsZipReaderCache... // Release might be called from multi-thread, we have to // take this function carefully to avoid delete-after-use.
MozExternalRefCountType nsJAR::Release(void) {
nsrefcnt count;
MOZ_ASSERT(0 != mRefCnt, "dup release");
RefPtr<nsZipReaderCache> cache; 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.
RecursiveMutexAutoLock lock(mLock);
cache = mCache;
mCache = nullptr;
} if (cache) {
DebugOnly<nsresult> rv = cache->ReleaseZip(this);
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to release zip file");
}
count = --mRefCnt; // don't access any member variable after this line
NS_LOG_RELEASE(this, count, "nsJAR"); if (0 == count) {
mRefCnt = 1; /* stabilize */ /* enable this to find non-threadsafe destructors: */ /* NS_ASSERT_OWNINGTHREAD(nsJAR); */ deletethis; return 0;
}
// The omnijar is special, it is opened early on and closed late
RefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile); if (!zip) {
zip = nsZipArchive::OpenArchive(zipFile);
}
mZip = zip; return mZip ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsJAR::Close() {
RecursiveMutexAutoLock lock(mLock);
LOG(("Close[%p]", this)); if (!mZip) { return NS_ERROR_FAILURE; // Never opened or already closed.
}
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
RecursiveMutexAutoLock lock(mLock); if (!mZip) { return NS_ERROR_FAILURE;
}
// Remove existing file or directory so we set permissions correctly. // If it's a directory that already exists and contains files, throw // an exception and return.
if (item->IsDirectory()) {
rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode()); // XXX Do this in nsZipArchive? It would be nice to keep extraction // XXX code completely there, but that would require a way to get a // XXX PRDir from outFile.
} else {
PRFileDesc* fd;
rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(),
&fd); if (NS_FAILED(rv)) return rv;
// ExtractFile also closes the fd handle and resolves the symlink if needed
rv = mZip->ExtractFile(item, outFile, fd);
} if (NS_FAILED(rv)) return rv;
// nsIFile needs milliseconds, while prtime is in microseconds. // non-fatal if this fails, ignore errors
outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
LOG(("GetInputStream[%p] %s", this, PromiseFlatCString(aEntryName).get())); // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case!
nsZipItem* item = nullptr; const nsCString& entry = PromiseFlatCString(aEntryName); if (*entry.get()) { // First check if item exists in jar
item = mZip->GetItem(entry); if (!item) return NS_ERROR_FILE_NOT_FOUND;
}
RefPtr<nsJARInputStream> jis = new nsJARInputStream();
RefPtr<nsZipHandle> handle = mZip->GetFD(); if (!handle) { return NS_ERROR_FAILURE;
}
return handle->GetNSPRFileDesc(aNSPRFileDesc);
}
//---------------------------------------------- // nsJAR private implementation //----------------------------------------------
nsresult nsJAR::LoadEntry(const nsACString& aFilename, nsCString& aBuf) { //-- Get a stream for reading the file
nsresult rv;
nsCOMPtr<nsIInputStream> manifestStream;
rv = GetInputStream(aFilename, getter_AddRefs(manifestStream)); if (NS_FAILED(rv)) return NS_ERROR_FILE_NOT_FOUND;
//-- Read the manifest file into memory char* buf;
uint64_t len64;
rv = manifestStream->Available(&len64); if (NS_FAILED(rv)) return rv;
NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_CORRUPTED); // bug 164695
uint32_t len = (uint32_t)len64;
buf = (char*)malloc(len + 1); if (!buf) return NS_ERROR_OUT_OF_MEMORY;
uint32_t bytesRead;
rv = manifestStream->Read(buf, len, &bytesRead); if (bytesRead != len) {
rv = NS_ERROR_FILE_CORRUPTED;
} if (NS_FAILED(rv)) {
free(buf); return rv;
}
buf[len] = '\0'; // Null-terminate the buffer
aBuf.Adopt(buf, len); return NS_OK;
}
int32_t nsJAR::ReadLine(constchar** src) { if (!*src) { return 0;
}
//--Moves pointer to beginning of next line and returns line length // not including CR/LF.
int32_t length; constchar* eol = strpbrk(*src, "\r\n");
if (eol == nullptr) // Probably reached end of file before newline
{
length = strlen(*src); if (length == 0) // immediate end-of-file
*src = nullptr; else// some data left on this line
*src += length;
} else {
length = eol - *src; if (eol[0] == '\r' && eol[1] == '\n') // CR LF, so skip 2
*src = eol + 2; else// Either CR or LF, so skip 1
*src = eol + 1;
} return length;
}
//---------------------------------------------- // nsJAREnumerator::HasMore //----------------------------------------------
NS_IMETHODIMP
nsJAREnumerator::HasMore(bool* aResult) { // try to get the next element if (!mName) {
NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind.");
nsresult rv = mFind->FindNext(&mName, &mNameLen); if (rv == NS_ERROR_FILE_NOT_FOUND) {
*aResult = false; // No more matches available return NS_OK;
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // no error translation
}
*aResult = true; return NS_OK;
}
//---------------------------------------------- // nsJAREnumerator::GetNext //----------------------------------------------
NS_IMETHODIMP
nsJAREnumerator::GetNext(nsACString& aResult) { // check if the current item is "stale" if (!mName) { bool bMore;
nsresult rv = HasMore(&bMore); if (NS_FAILED(rv) || !bMore) return NS_ERROR_FAILURE; // no error translation
}
aResult.Assign(mName, mNameLen);
mName = 0; // we just gave this one away return NS_OK;
}
zip->ClearReleaseTime();
rv = zip->GetNSPRFileDesc(aRetVal); // Do this to avoid possible deadlock on mLock with ReleaseZip().
{
MutexAutoUnlock unlock(mLock);
zip = nullptr;
} return rv; #endif/* XP_WIN */
}
// It is possible that two thread compete for this zip. The dangerous // case is where one thread Releases the zip and discovers that the ref // count has gone to one. Before it can call this ReleaseZip method // another thread calls our GetZip method. The ref count goes to two. That // second thread then Releases the zip and the ref count goes to one. It // then tries to enter this ReleaseZip method and blocks while the first // thread is still here. The first thread continues and remove the zip from // the cache and calls its Release method sending the ref count to 0 and // deleting the zip. However, the second thread is still blocked at the // start of ReleaseZip, but the 'zip' param now hold a reference to a // deleted zip! // // So, we are going to try safeguarding here by searching our hashtable while // locked here for the zip. We return fast if it is not found.
bool found = false; for (constauto& current : mZips.Values()) { if (zip == current) {
found = true; break;
}
}
if (!found) { #ifdef ZIP_CACHE_HIT_RATE
mZipSyncMisses++; #endif return NS_OK;
}
zip->SetReleaseTime();
if (mZips.Count() <= mCacheSize) return NS_OK;
// Find the oldest zip.
nsJAR* oldest = nullptr; for (constauto& current : mZips.Values()) {
PRIntervalTime currentReleaseTime = current->GetReleaseTime(); if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) { if (oldest == nullptr || currentReleaseTime < oldest->GetReleaseTime()) {
oldest = current;
}
}
}
// Because of the craziness above it is possible that there is no zip that // needs removing. if (!oldest) return NS_OK;
// remove from hashtable
nsAutoCString uri;
rv = oldest->GetFullJarPath(uri); if (NS_FAILED(rv)) { return rv;
}
// 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.
RefPtr<nsJAR> removed;
mZips.Remove(uri, getter_AddRefs(removed));
NS_ASSERTION(removed, "botched");
NS_ASSERTION(oldest == removed, "removed wrong entry");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.