modules/libjar/nsJAR.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include <string.h>
     6 #include "nsJARInputStream.h"
     7 #include "nsJAR.h"
     8 #include "nsIFile.h"
     9 #include "nsIConsoleService.h"
    10 #include "nsICryptoHash.h"
    11 #include "prprf.h"
    12 #include "mozilla/Omnijar.h"
    14 #ifdef XP_UNIX
    15   #include <sys/stat.h>
    16 #elif defined (XP_WIN)
    17   #include <io.h>
    18 #endif
    20 using namespace mozilla;
    22 //----------------------------------------------
    23 // nsJARManifestItem declaration
    24 //----------------------------------------------
    25 /*
    26  * nsJARManifestItem contains meta-information pertaining
    27  * to an individual JAR entry, taken from the
    28  * META-INF/MANIFEST.MF and META-INF/ *.SF files.
    29  * This is security-critical information, defined here so it is not
    30  * accessible from anywhere else.
    31  */
    32 typedef enum
    33 {
    34   JAR_INVALID       = 1,
    35   JAR_INTERNAL      = 2,
    36   JAR_EXTERNAL      = 3
    37 } JARManifestItemType;
    39 class nsJARManifestItem
    40 {
    41 public:
    42   JARManifestItemType mType;
    44   // True if the second step of verification (VerifyEntry)
    45   // has taken place:
    46   bool                entryVerified;
    48   // Not signed, valid, or failure code
    49   int16_t             status;
    51   // Internal storage of digests
    52   nsCString           calculatedSectionDigest;
    53   nsCString           storedEntryDigest;
    55   nsJARManifestItem();
    56   virtual ~nsJARManifestItem();
    57 };
    59 //-------------------------------------------------
    60 // nsJARManifestItem constructors and destructor
    61 //-------------------------------------------------
    62 nsJARManifestItem::nsJARManifestItem(): mType(JAR_INTERNAL),
    63                                         entryVerified(false),
    64                                         status(JAR_NOT_SIGNED)
    65 {
    66 }
    68 nsJARManifestItem::~nsJARManifestItem()
    69 {
    70 }
    72 //----------------------------------------------
    73 // nsJAR constructor/destructor
    74 //----------------------------------------------
    76 // The following initialization makes a guess of 10 entries per jarfile.
    77 nsJAR::nsJAR(): mZip(new nsZipArchive()),
    78                 mManifestData(10),
    79                 mParsedManifest(false),
    80                 mGlobalStatus(JAR_MANIFEST_NOT_PARSED),
    81                 mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
    82                 mCache(nullptr),
    83                 mLock("nsJAR::mLock"),
    84                 mTotalItemsInManifest(0),
    85                 mOpened(false)
    86 {
    87 }
    89 nsJAR::~nsJAR()
    90 {
    91   Close();
    92 }
    94 NS_IMPL_QUERY_INTERFACE(nsJAR, nsIZipReader)
    95 NS_IMPL_ADDREF(nsJAR)
    97 // Custom Release method works with nsZipReaderCache...
    98 MozExternalRefCountType nsJAR::Release(void)
    99 {
   100   nsrefcnt count;
   101   NS_PRECONDITION(0 != mRefCnt, "dup release");
   102   count = --mRefCnt;
   103   NS_LOG_RELEASE(this, count, "nsJAR");
   104   if (0 == count) {
   105     mRefCnt = 1; /* stabilize */
   106     /* enable this to find non-threadsafe destructors: */
   107     /* NS_ASSERT_OWNINGTHREAD(nsJAR); */
   108     delete this;
   109     return 0;
   110   }
   111   else if (1 == count && mCache) {
   112 #ifdef DEBUG
   113     nsresult rv =
   114 #endif
   115       mCache->ReleaseZip(this);
   116     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to release zip file");
   117   }
   118   return count;
   119 }
   121 //----------------------------------------------
   122 // nsIZipReader implementation
   123 //----------------------------------------------
   125 NS_IMETHODIMP
   126 nsJAR::Open(nsIFile* zipFile)
   127 {
   128   NS_ENSURE_ARG_POINTER(zipFile);
   129   if (mOpened) return NS_ERROR_FAILURE; // Already open!
   131   mZipFile = zipFile;
   132   mOuterZipEntry.Truncate();
   133   mOpened = true;
   135   // The omnijar is special, it is opened early on and closed late
   136   // this avoids reopening it
   137   nsRefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile);
   138   if (zip) {
   139     mZip = zip;
   140     return NS_OK;
   141   }
   142   return mZip->OpenArchive(zipFile);
   143 }
   145 NS_IMETHODIMP
   146 nsJAR::OpenInner(nsIZipReader *aZipReader, const nsACString &aZipEntry)
   147 {
   148   NS_ENSURE_ARG_POINTER(aZipReader);
   149   if (mOpened) return NS_ERROR_FAILURE; // Already open!
   151   bool exist;
   152   nsresult rv = aZipReader->HasEntry(aZipEntry, &exist);
   153   NS_ENSURE_SUCCESS(rv, rv);
   154   NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND);
   156   rv = aZipReader->GetFile(getter_AddRefs(mZipFile));
   157   NS_ENSURE_SUCCESS(rv, rv);
   159   mOpened = true;
   161   mOuterZipEntry.Assign(aZipEntry);
   163   nsRefPtr<nsZipHandle> handle;
   164   rv = nsZipHandle::Init(static_cast<nsJAR*>(aZipReader)->mZip.get(), PromiseFlatCString(aZipEntry).get(),
   165                          getter_AddRefs(handle));
   166   if (NS_FAILED(rv))
   167     return rv;
   169   return mZip->OpenArchive(handle);
   170 }
   172 NS_IMETHODIMP
   173 nsJAR::GetFile(nsIFile* *result)
   174 {
   175   *result = mZipFile;
   176   NS_IF_ADDREF(*result);
   177   return NS_OK;
   178 }
   180 NS_IMETHODIMP
   181 nsJAR::Close()
   182 {
   183   mOpened = false;
   184   mParsedManifest = false;
   185   mManifestData.Clear();
   186   mGlobalStatus = JAR_MANIFEST_NOT_PARSED;
   187   mTotalItemsInManifest = 0;
   189   nsRefPtr<nsZipArchive> greOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
   190   nsRefPtr<nsZipArchive> appOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
   192   if (mZip == greOmni || mZip == appOmni) {
   193     mZip = new nsZipArchive();
   194     return NS_OK;
   195   }
   196   return mZip->CloseArchive();
   197 }
   199 NS_IMETHODIMP
   200 nsJAR::Test(const nsACString &aEntryName)
   201 {
   202   return mZip->Test(aEntryName.IsEmpty()? nullptr : PromiseFlatCString(aEntryName).get());
   203 }
   205 NS_IMETHODIMP
   206 nsJAR::Extract(const nsACString &aEntryName, nsIFile* outFile)
   207 {
   208   // nsZipArchive and zlib are not thread safe
   209   // we need to use a lock to prevent bug #51267
   210   MutexAutoLock lock(mLock);
   212   nsZipItem *item = mZip->GetItem(PromiseFlatCString(aEntryName).get());
   213   NS_ENSURE_TRUE(item, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
   215   // Remove existing file or directory so we set permissions correctly.
   216   // If it's a directory that already exists and contains files, throw
   217   // an exception and return.
   219   nsresult rv = outFile->Remove(false);
   220   if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY ||
   221       rv == NS_ERROR_FAILURE)
   222     return rv;
   224   if (item->IsDirectory())
   225   {
   226     rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode());
   227     //XXX Do this in nsZipArchive?  It would be nice to keep extraction
   228     //XXX code completely there, but that would require a way to get a
   229     //XXX PRDir from outFile.
   230   }
   231   else
   232   {
   233     PRFileDesc* fd;
   234     rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(), &fd);
   235     if (NS_FAILED(rv)) return rv;
   237     // ExtractFile also closes the fd handle and resolves the symlink if needed
   238     nsAutoCString path;
   239     rv = outFile->GetNativePath(path);
   240     if (NS_FAILED(rv)) return rv;
   242     rv = mZip->ExtractFile(item, path.get(), fd);
   243   }
   244   if (NS_FAILED(rv)) return rv;
   246   // nsIFile needs milliseconds, while prtime is in microseconds.
   247   // non-fatal if this fails, ignore errors
   248   outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
   250   return NS_OK;
   251 }
   253 NS_IMETHODIMP
   254 nsJAR::GetEntry(const nsACString &aEntryName, nsIZipEntry* *result)
   255 {
   256   nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get());
   257   NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
   259   nsJARItem* jarItem = new nsJARItem(zipItem);
   261   NS_ADDREF(*result = jarItem);
   262   return NS_OK;
   263 }
   265 NS_IMETHODIMP
   266 nsJAR::HasEntry(const nsACString &aEntryName, bool *result)
   267 {
   268   *result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr;
   269   return NS_OK;
   270 }
   272 NS_IMETHODIMP
   273 nsJAR::FindEntries(const nsACString &aPattern, nsIUTF8StringEnumerator **result)
   274 {
   275   NS_ENSURE_ARG_POINTER(result);
   277   nsZipFind *find;
   278   nsresult rv = mZip->FindInit(aPattern.IsEmpty()? nullptr : PromiseFlatCString(aPattern).get(), &find);
   279   NS_ENSURE_SUCCESS(rv, rv);
   281   nsIUTF8StringEnumerator *zipEnum = new nsJAREnumerator(find);
   283   NS_ADDREF(*result = zipEnum);
   284   return NS_OK;
   285 }
   287 NS_IMETHODIMP
   288 nsJAR::GetInputStream(const nsACString &aFilename, nsIInputStream** result)
   289 {
   290   return GetInputStreamWithSpec(EmptyCString(), aFilename, result);
   291 }
   293 NS_IMETHODIMP
   294 nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
   295                           const nsACString &aEntryName, nsIInputStream** result)
   296 {
   297   NS_ENSURE_ARG_POINTER(result);
   299   // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case!
   300   nsZipItem *item = nullptr;
   301   const char *entry = PromiseFlatCString(aEntryName).get();
   302   if (*entry) {
   303     // First check if item exists in jar
   304     item = mZip->GetItem(entry);
   305     if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   306   }
   307   nsJARInputStream* jis = new nsJARInputStream();
   308   // addref now so we can call InitFile/InitDirectory()
   309   NS_ADDREF(*result = jis);
   311   nsresult rv = NS_OK;
   312   if (!item || item->IsDirectory()) {
   313     rv = jis->InitDirectory(this, aJarDirSpec, entry);
   314   } else {
   315     rv = jis->InitFile(this, item);
   316   }
   317   if (NS_FAILED(rv)) {
   318     NS_RELEASE(*result);
   319   }
   320   return rv;
   321 }
   323 NS_IMETHODIMP
   324 nsJAR::GetCertificatePrincipal(const nsACString &aFilename, nsICertificatePrincipal** aPrincipal)
   325 {
   326   //-- Parameter check
   327   if (!aPrincipal)
   328     return NS_ERROR_NULL_POINTER;
   329   *aPrincipal = nullptr;
   331   // Don't check signatures in the omnijar - this is only
   332   // interesting for extensions/XPIs.
   333   nsRefPtr<nsZipArchive> greOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
   334   nsRefPtr<nsZipArchive> appOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
   336   if (mZip == greOmni || mZip == appOmni)
   337     return NS_OK;
   339   //-- Parse the manifest
   340   nsresult rv = ParseManifest();
   341   if (NS_FAILED(rv)) return rv;
   342   if (mGlobalStatus == JAR_NO_MANIFEST)
   343     return NS_OK;
   345   int16_t requestedStatus;
   346   if (!aFilename.IsEmpty())
   347   {
   348     //-- Find the item
   349     nsJARManifestItem* manItem = mManifestData.Get(aFilename);
   350     if (!manItem)
   351       return NS_OK;
   352     //-- Verify the item against the manifest
   353     if (!manItem->entryVerified)
   354     {
   355       nsXPIDLCString entryData;
   356       uint32_t entryDataLen;
   357       rv = LoadEntry(aFilename, getter_Copies(entryData), &entryDataLen);
   358       if (NS_FAILED(rv)) return rv;
   359       rv = VerifyEntry(manItem, entryData, entryDataLen);
   360       if (NS_FAILED(rv)) return rv;
   361     }
   362     requestedStatus = manItem->status;
   363   }
   364   else // User wants identity of signer w/o verifying any entries
   365     requestedStatus = mGlobalStatus;
   367   if (requestedStatus != JAR_VALID_MANIFEST)
   368     ReportError(aFilename, requestedStatus);
   369   else // Valid signature
   370   {
   371     *aPrincipal = mPrincipal;
   372     NS_IF_ADDREF(*aPrincipal);
   373   }
   374   return NS_OK;
   375 }
   377 NS_IMETHODIMP
   378 nsJAR::GetManifestEntriesCount(uint32_t* count)
   379 {
   380   *count = mTotalItemsInManifest;
   381   return NS_OK;
   382 }
   384 nsresult
   385 nsJAR::GetJarPath(nsACString& aResult)
   386 {
   387   NS_ENSURE_ARG_POINTER(mZipFile);
   389   return mZipFile->GetNativePath(aResult);
   390 }
   392 //----------------------------------------------
   393 // nsJAR private implementation
   394 //----------------------------------------------
   395 nsresult
   396 nsJAR::LoadEntry(const nsACString &aFilename, char** aBuf, uint32_t* aBufLen)
   397 {
   398   //-- Get a stream for reading the file
   399   nsresult rv;
   400   nsCOMPtr<nsIInputStream> manifestStream;
   401   rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
   402   if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   404   //-- Read the manifest file into memory
   405   char* buf;
   406   uint64_t len64;
   407   rv = manifestStream->Available(&len64);
   408   if (NS_FAILED(rv)) return rv;
   409   NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_CORRUPTED); // bug 164695
   410   uint32_t len = (uint32_t)len64;
   411   buf = (char*)malloc(len+1);
   412   if (!buf) return NS_ERROR_OUT_OF_MEMORY;
   413   uint32_t bytesRead;
   414   rv = manifestStream->Read(buf, len, &bytesRead);
   415   if (bytesRead != len)
   416     rv = NS_ERROR_FILE_CORRUPTED;
   417   if (NS_FAILED(rv)) {
   418     free(buf);
   419     return rv;
   420   }
   421   buf[len] = '\0'; //Null-terminate the buffer
   422   *aBuf = buf;
   423   if (aBufLen)
   424     *aBufLen = len;
   425   return NS_OK;
   426 }
   429 int32_t
   430 nsJAR::ReadLine(const char** src)
   431 {
   432   if (!*src) {
   433     return 0;
   434   }
   436   //--Moves pointer to beginning of next line and returns line length
   437   //  not including CR/LF.
   438   int32_t length;
   439   char* eol = PL_strpbrk(*src, "\r\n");
   441   if (eol == nullptr) // Probably reached end of file before newline
   442   {
   443     length = strlen(*src);
   444     if (length == 0) // immediate end-of-file
   445       *src = nullptr;
   446     else             // some data left on this line
   447       *src += length;
   448   }
   449   else
   450   {
   451     length = eol - *src;
   452     if (eol[0] == '\r' && eol[1] == '\n')      // CR LF, so skip 2
   453       *src = eol+2;
   454     else                                       // Either CR or LF, so skip 1
   455       *src = eol+1;
   456   }
   457   return length;
   458 }
   460 //-- The following #defines are used by ParseManifest()
   461 //   and ParseOneFile(). The header strings are defined in the JAR specification.
   462 #define JAR_MF 1
   463 #define JAR_SF 2
   464 #define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$"
   465 #define JAR_SF_SEARCH_STRING "(M|/M)ETA-INF/*.(SF|sf)$"
   466 #define JAR_MF_HEADER (const char*)"Manifest-Version: 1.0"
   467 #define JAR_SF_HEADER (const char*)"Signature-Version: 1.0"
   469 nsresult
   470 nsJAR::ParseManifest()
   471 {
   472   //-- Verification Step 1
   473   if (mParsedManifest)
   474     return NS_OK;
   475   //-- (1)Manifest (MF) file
   476   nsCOMPtr<nsIUTF8StringEnumerator> files;
   477   nsresult rv = FindEntries(nsDependentCString(JAR_MF_SEARCH_STRING), getter_AddRefs(files));
   478   if (!files) rv = NS_ERROR_FAILURE;
   479   if (NS_FAILED(rv)) return rv;
   481   //-- Load the file into memory
   482   bool more;
   483   rv = files->HasMore(&more);
   484   NS_ENSURE_SUCCESS(rv, rv);
   485   if (!more)
   486   {
   487     mGlobalStatus = JAR_NO_MANIFEST;
   488     mParsedManifest = true;
   489     return NS_OK;
   490   }
   492   nsAutoCString manifestFilename;
   493   rv = files->GetNext(manifestFilename);
   494   NS_ENSURE_SUCCESS(rv, rv);
   496   // Check if there is more than one manifest, if so then error!
   497   rv = files->HasMore(&more);
   498   if (NS_FAILED(rv)) return rv;
   499   if (more)
   500   {
   501     mParsedManifest = true;
   502     return NS_ERROR_FILE_CORRUPTED; // More than one MF file
   503   }
   505   nsXPIDLCString manifestBuffer;
   506   uint32_t manifestLen;
   507   rv = LoadEntry(manifestFilename, getter_Copies(manifestBuffer), &manifestLen);
   508   if (NS_FAILED(rv)) return rv;
   510   //-- Parse it
   511   rv = ParseOneFile(manifestBuffer, JAR_MF);
   512   if (NS_FAILED(rv)) return rv;
   514   //-- (2)Signature (SF) file
   515   // If there are multiple signatures, we select one.
   516   rv = FindEntries(nsDependentCString(JAR_SF_SEARCH_STRING), getter_AddRefs(files));
   517   if (!files) rv = NS_ERROR_FAILURE;
   518   if (NS_FAILED(rv)) return rv;
   519   //-- Get an SF file
   520   rv = files->HasMore(&more);
   521   if (NS_FAILED(rv)) return rv;
   522   if (!more)
   523   {
   524     mGlobalStatus = JAR_NO_MANIFEST;
   525     mParsedManifest = true;
   526     return NS_OK;
   527   }
   528   rv = files->GetNext(manifestFilename);
   529   if (NS_FAILED(rv)) return rv;
   531   rv = LoadEntry(manifestFilename, getter_Copies(manifestBuffer), &manifestLen);
   532   if (NS_FAILED(rv)) return rv;
   534   //-- Get its corresponding signature file
   535   nsAutoCString sigFilename(manifestFilename);
   536   int32_t extension = sigFilename.RFindChar('.') + 1;
   537   NS_ASSERTION(extension != 0, "Manifest Parser: Missing file extension.");
   538   (void)sigFilename.Cut(extension, 2);
   539   nsXPIDLCString sigBuffer;
   540   uint32_t sigLen;
   541   {
   542     nsAutoCString tempFilename(sigFilename); tempFilename.Append("rsa", 3);
   543     rv = LoadEntry(tempFilename, getter_Copies(sigBuffer), &sigLen);
   544   }
   545   if (NS_FAILED(rv))
   546   {
   547     nsAutoCString tempFilename(sigFilename); tempFilename.Append("RSA", 3);
   548     rv = LoadEntry(tempFilename, getter_Copies(sigBuffer), &sigLen);
   549   }
   550   if (NS_FAILED(rv))
   551   {
   552     mGlobalStatus = JAR_NO_MANIFEST;
   553     mParsedManifest = true;
   554     return NS_OK;
   555   }
   557   //-- Get the signature verifier service
   558   nsCOMPtr<nsISignatureVerifier> verifier =
   559            do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
   560   if (NS_FAILED(rv)) // No signature verifier available
   561   {
   562     mGlobalStatus = JAR_NO_MANIFEST;
   563     mParsedManifest = true;
   564     return NS_OK;
   565   }
   567   //-- Verify that the signature file is a valid signature of the SF file
   568   int32_t verifyError;
   569   rv = verifier->VerifySignature(sigBuffer, sigLen, manifestBuffer, manifestLen,
   570                                  &verifyError, getter_AddRefs(mPrincipal));
   571   if (NS_FAILED(rv)) return rv;
   572   if (mPrincipal && verifyError == 0)
   573     mGlobalStatus = JAR_VALID_MANIFEST;
   574   else if (verifyError == nsISignatureVerifier::VERIFY_ERROR_UNKNOWN_CA)
   575     mGlobalStatus = JAR_INVALID_UNKNOWN_CA;
   576   else
   577     mGlobalStatus = JAR_INVALID_SIG;
   579   //-- Parse the SF file. If the verification above failed, principal
   580   // is null, and ParseOneFile will mark the relevant entries as invalid.
   581   // if ParseOneFile fails, then it has no effect, and we can safely
   582   // continue to the next SF file, or return.
   583   ParseOneFile(manifestBuffer, JAR_SF);
   584   mParsedManifest = true;
   586   return NS_OK;
   587 }
   589 nsresult
   590 nsJAR::ParseOneFile(const char* filebuf, int16_t aFileType)
   591 {
   592   //-- Check file header
   593   const char* nextLineStart = filebuf;
   594   nsAutoCString curLine;
   595   int32_t linelen;
   596   linelen = ReadLine(&nextLineStart);
   597   curLine.Assign(filebuf, linelen);
   599   if ( ((aFileType == JAR_MF) && !curLine.Equals(JAR_MF_HEADER) ) ||
   600        ((aFileType == JAR_SF) && !curLine.Equals(JAR_SF_HEADER) ) )
   601      return NS_ERROR_FILE_CORRUPTED;
   603   //-- Skip header section
   604   do {
   605     linelen = ReadLine(&nextLineStart);
   606   } while (linelen > 0);
   608   //-- Set up parsing variables
   609   const char* curPos;
   610   const char* sectionStart = nextLineStart;
   612   nsJARManifestItem* curItemMF = nullptr;
   613   bool foundName = false;
   614   if (aFileType == JAR_MF) {
   615     curItemMF = new nsJARManifestItem();
   616   }
   618   nsAutoCString curItemName;
   619   nsAutoCString storedSectionDigest;
   621   for(;;)
   622   {
   623     curPos = nextLineStart;
   624     linelen = ReadLine(&nextLineStart);
   625     curLine.Assign(curPos, linelen);
   626     if (linelen == 0)
   627     // end of section (blank line or end-of-file)
   628     {
   629       if (aFileType == JAR_MF)
   630       {
   631         mTotalItemsInManifest++;
   632         if (curItemMF->mType != JAR_INVALID)
   633         {
   634           //-- Did this section have a name: line?
   635           if(!foundName)
   636             curItemMF->mType = JAR_INVALID;
   637           else
   638           {
   639             //-- If it's an internal item, it must correspond
   640             //   to a valid jar entry
   641             if (curItemMF->mType == JAR_INTERNAL)
   642             {
   643               bool exists;
   644               nsresult rv = HasEntry(curItemName, &exists);
   645               if (NS_FAILED(rv) || !exists)
   646                 curItemMF->mType = JAR_INVALID;
   647             }
   648             //-- Check for duplicates
   649             if (mManifestData.Contains(curItemName)) {
   650               curItemMF->mType = JAR_INVALID;
   651             }
   652           }
   653         }
   655         if (curItemMF->mType == JAR_INVALID)
   656           delete curItemMF;
   657         else //-- calculate section digest
   658         {
   659           uint32_t sectionLength = curPos - sectionStart;
   660           CalculateDigest(sectionStart, sectionLength,
   661                           curItemMF->calculatedSectionDigest);
   662           //-- Save item in the hashtable
   663           mManifestData.Put(curItemName, curItemMF);
   664         }
   665         if (nextLineStart == nullptr) // end-of-file
   666           break;
   668         sectionStart = nextLineStart;
   669         curItemMF = new nsJARManifestItem();
   670       } // (aFileType == JAR_MF)
   671       else
   672         //-- file type is SF, compare digest with calculated
   673         //   section digests from MF file.
   674       {
   675         if (foundName)
   676         {
   677           nsJARManifestItem* curItemSF = mManifestData.Get(curItemName);
   678           if(curItemSF)
   679           {
   680             NS_ASSERTION(curItemSF->status == JAR_NOT_SIGNED,
   681                          "SECURITY ERROR: nsJARManifestItem not correctly initialized");
   682             curItemSF->status = mGlobalStatus;
   683             if (curItemSF->status == JAR_VALID_MANIFEST)
   684             { // Compare digests
   685               if (storedSectionDigest.IsEmpty())
   686                 curItemSF->status = JAR_NOT_SIGNED;
   687               else
   688               {
   689                 if (!storedSectionDigest.Equals(curItemSF->calculatedSectionDigest))
   690                   curItemSF->status = JAR_INVALID_MANIFEST;
   691                 curItemSF->calculatedSectionDigest.Truncate();
   692                 storedSectionDigest.Truncate();
   693               }
   694             } // (aPrincipal != nullptr)
   695           } // if(curItemSF)
   696         } // if(foundName)
   698         if(nextLineStart == nullptr) // end-of-file
   699           break;
   700       } // aFileType == JAR_SF
   701       foundName = false;
   702       continue;
   703     } // if(linelen == 0)
   705     //-- Look for continuations (beginning with a space) on subsequent lines
   706     //   and append them to the current line.
   707     while(*nextLineStart == ' ')
   708     {
   709       curPos = nextLineStart;
   710       int32_t continuationLen = ReadLine(&nextLineStart) - 1;
   711       nsAutoCString continuation(curPos+1, continuationLen);
   712       curLine += continuation;
   713       linelen += continuationLen;
   714     }
   716     //-- Find colon in current line, this separates name from value
   717     int32_t colonPos = curLine.FindChar(':');
   718     if (colonPos == -1)    // No colon on line, ignore line
   719       continue;
   720     //-- Break down the line
   721     nsAutoCString lineName;
   722     curLine.Left(lineName, colonPos);
   723     nsAutoCString lineData;
   724     curLine.Mid(lineData, colonPos+2, linelen - (colonPos+2));
   726     //-- Lines to look for:
   727     // (1) Digest:
   728     if (lineName.LowerCaseEqualsLiteral("sha1-digest"))
   729     //-- This is a digest line, save the data in the appropriate place
   730     {
   731       if(aFileType == JAR_MF)
   732         curItemMF->storedEntryDigest = lineData;
   733       else
   734         storedSectionDigest = lineData;
   735       continue;
   736     }
   738     // (2) Name: associates this manifest section with a file in the jar.
   739     if (!foundName && lineName.LowerCaseEqualsLiteral("name"))
   740     {
   741       curItemName = lineData;
   742       foundName = true;
   743       continue;
   744     }
   746     // (3) Magic: this may be an inline Javascript.
   747     //     We can't do any other kind of magic.
   748     if (aFileType == JAR_MF && lineName.LowerCaseEqualsLiteral("magic"))
   749     {
   750       if (lineData.LowerCaseEqualsLiteral("javascript"))
   751         curItemMF->mType = JAR_EXTERNAL;
   752       else
   753         curItemMF->mType = JAR_INVALID;
   754       continue;
   755     }
   757   } // for (;;)
   758   return NS_OK;
   759 } //ParseOneFile()
   761 nsresult
   762 nsJAR::VerifyEntry(nsJARManifestItem* aManItem, const char* aEntryData,
   763                    uint32_t aLen)
   764 {
   765   if (aManItem->status == JAR_VALID_MANIFEST)
   766   {
   767     if (aManItem->storedEntryDigest.IsEmpty())
   768       // No entry digests in manifest file. Entry is unsigned.
   769       aManItem->status = JAR_NOT_SIGNED;
   770     else
   771     { //-- Calculate and compare digests
   772       nsCString calculatedEntryDigest;
   773       nsresult rv = CalculateDigest(aEntryData, aLen, calculatedEntryDigest);
   774       if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   775       if (!aManItem->storedEntryDigest.Equals(calculatedEntryDigest))
   776         aManItem->status = JAR_INVALID_ENTRY;
   777       aManItem->storedEntryDigest.Truncate();
   778     }
   779   }
   780   aManItem->entryVerified = true;
   781   return NS_OK;
   782 }
   784 void nsJAR::ReportError(const nsACString &aFilename, int16_t errorCode)
   785 {
   786   //-- Generate error message
   787   nsAutoString message;
   788   message.AssignLiteral("Signature Verification Error: the signature on ");
   789   if (!aFilename.IsEmpty())
   790     AppendASCIItoUTF16(aFilename, message);
   791   else
   792     message.AppendLiteral("this .jar archive");
   793   message.AppendLiteral(" is invalid because ");
   794   switch(errorCode)
   795   {
   796   case JAR_NOT_SIGNED:
   797     message.AppendLiteral("the archive did not contain a valid PKCS7 signature.");
   798     break;
   799   case JAR_INVALID_SIG:
   800     message.AppendLiteral("the digital signature (*.RSA) file is not a valid signature of the signature instruction file (*.SF).");
   801     break;
   802   case JAR_INVALID_UNKNOWN_CA:
   803     message.AppendLiteral("the certificate used to sign this file has an unrecognized issuer.");
   804     break;
   805   case JAR_INVALID_MANIFEST:
   806     message.AppendLiteral("the signature instruction file (*.SF) does not contain a valid hash of the MANIFEST.MF file.");
   807     break;
   808   case JAR_INVALID_ENTRY:
   809     message.AppendLiteral("the MANIFEST.MF file does not contain a valid hash of the file being verified.");
   810     break;
   811   case JAR_NO_MANIFEST:
   812     message.AppendLiteral("the archive did not contain a manifest.");
   813     break;
   814   default:
   815     message.AppendLiteral("of an unknown problem.");
   816   }
   818   // Report error in JS console
   819   nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
   820   if (console)
   821   {
   822     console->LogStringMessage(message.get());
   823   }
   824 #ifdef DEBUG
   825   char* messageCstr = ToNewCString(message);
   826   if (!messageCstr) return;
   827   fprintf(stderr, "%s\n", messageCstr);
   828   nsMemory::Free(messageCstr);
   829 #endif
   830 }
   833 nsresult nsJAR::CalculateDigest(const char* aInBuf, uint32_t aLen,
   834                                 nsCString& digest)
   835 {
   836   nsresult rv;
   838   nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
   839   if (NS_FAILED(rv)) return rv;
   841   rv = hasher->Init(nsICryptoHash::SHA1);
   842   if (NS_FAILED(rv)) return rv;
   844   rv = hasher->Update((const uint8_t*) aInBuf, aLen);
   845   if (NS_FAILED(rv)) return rv;
   847   return hasher->Finish(true, digest);
   848 }
   850 NS_IMPL_ISUPPORTS(nsJAREnumerator, nsIUTF8StringEnumerator)
   852 //----------------------------------------------
   853 // nsJAREnumerator::HasMore
   854 //----------------------------------------------
   855 NS_IMETHODIMP
   856 nsJAREnumerator::HasMore(bool* aResult)
   857 {
   858     // try to get the next element
   859     if (!mName) {
   860         NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind.");
   861         nsresult rv = mFind->FindNext( &mName, &mNameLen );
   862         if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
   863             *aResult = false;                    // No more matches available
   864             return NS_OK;
   865         }
   866         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);    // no error translation
   867     }
   869     *aResult = true;
   870     return NS_OK;
   871 }
   873 //----------------------------------------------
   874 // nsJAREnumerator::GetNext
   875 //----------------------------------------------
   876 NS_IMETHODIMP
   877 nsJAREnumerator::GetNext(nsACString& aResult)
   878 {
   879     // check if the current item is "stale"
   880     if (!mName) {
   881         bool     bMore;
   882         nsresult rv = HasMore(&bMore);
   883         if (NS_FAILED(rv) || !bMore)
   884             return NS_ERROR_FAILURE; // no error translation
   885     }
   886     aResult.Assign(mName, mNameLen);
   887     mName = 0; // we just gave this one away
   888     return NS_OK;
   889 }
   892 NS_IMPL_ISUPPORTS(nsJARItem, nsIZipEntry)
   894 nsJARItem::nsJARItem(nsZipItem* aZipItem)
   895     : mSize(aZipItem->Size()),
   896       mRealsize(aZipItem->RealSize()),
   897       mCrc32(aZipItem->CRC32()),
   898       mLastModTime(aZipItem->LastModTime()),
   899       mCompression(aZipItem->Compression()),
   900       mPermissions(aZipItem->Mode()),
   901       mIsDirectory(aZipItem->IsDirectory()),
   902       mIsSynthetic(aZipItem->isSynthetic)
   903 {
   904 }
   906 //------------------------------------------
   907 // nsJARItem::GetCompression
   908 //------------------------------------------
   909 NS_IMETHODIMP
   910 nsJARItem::GetCompression(uint16_t *aCompression)
   911 {
   912     NS_ENSURE_ARG_POINTER(aCompression);
   914     *aCompression = mCompression;
   915     return NS_OK;
   916 }
   918 //------------------------------------------
   919 // nsJARItem::GetSize
   920 //------------------------------------------
   921 NS_IMETHODIMP
   922 nsJARItem::GetSize(uint32_t *aSize)
   923 {
   924     NS_ENSURE_ARG_POINTER(aSize);
   926     *aSize = mSize;
   927     return NS_OK;
   928 }
   930 //------------------------------------------
   931 // nsJARItem::GetRealSize
   932 //------------------------------------------
   933 NS_IMETHODIMP
   934 nsJARItem::GetRealSize(uint32_t *aRealsize)
   935 {
   936     NS_ENSURE_ARG_POINTER(aRealsize);
   938     *aRealsize = mRealsize;
   939     return NS_OK;
   940 }
   942 //------------------------------------------
   943 // nsJARItem::GetCrc32
   944 //------------------------------------------
   945 NS_IMETHODIMP
   946 nsJARItem::GetCRC32(uint32_t *aCrc32)
   947 {
   948     NS_ENSURE_ARG_POINTER(aCrc32);
   950     *aCrc32 = mCrc32;
   951     return NS_OK;
   952 }
   954 //------------------------------------------
   955 // nsJARItem::GetIsDirectory
   956 //------------------------------------------
   957 NS_IMETHODIMP
   958 nsJARItem::GetIsDirectory(bool *aIsDirectory)
   959 {
   960     NS_ENSURE_ARG_POINTER(aIsDirectory);
   962     *aIsDirectory = mIsDirectory;
   963     return NS_OK;
   964 }
   966 //------------------------------------------
   967 // nsJARItem::GetIsSynthetic
   968 //------------------------------------------
   969 NS_IMETHODIMP
   970 nsJARItem::GetIsSynthetic(bool *aIsSynthetic)
   971 {
   972     NS_ENSURE_ARG_POINTER(aIsSynthetic);
   974     *aIsSynthetic = mIsSynthetic;
   975     return NS_OK;
   976 }
   978 //------------------------------------------
   979 // nsJARItem::GetLastModifiedTime
   980 //------------------------------------------
   981 NS_IMETHODIMP
   982 nsJARItem::GetLastModifiedTime(PRTime* aLastModTime)
   983 {
   984     NS_ENSURE_ARG_POINTER(aLastModTime);
   986     *aLastModTime = mLastModTime;
   987     return NS_OK;
   988 }
   990 //------------------------------------------
   991 // nsJARItem::GetPermissions
   992 //------------------------------------------
   993 NS_IMETHODIMP
   994 nsJARItem::GetPermissions(uint32_t* aPermissions)
   995 {
   996     NS_ENSURE_ARG_POINTER(aPermissions);
   998     *aPermissions = mPermissions;
   999     return NS_OK;
  1002 ////////////////////////////////////////////////////////////////////////////////
  1003 // nsIZipReaderCache
  1005 NS_IMPL_ISUPPORTS(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
  1007 nsZipReaderCache::nsZipReaderCache()
  1008   : mLock("nsZipReaderCache.mLock")
  1009   , mZips(16)
  1010 #ifdef ZIP_CACHE_HIT_RATE
  1012     mZipCacheLookups(0),
  1013     mZipCacheHits(0),
  1014     mZipCacheFlushes(0),
  1015     mZipSyncMisses(0)
  1016 #endif
  1020 NS_IMETHODIMP
  1021 nsZipReaderCache::Init(uint32_t cacheSize)
  1023   mCacheSize = cacheSize;
  1025 // Register as a memory pressure observer
  1026   nsCOMPtr<nsIObserverService> os =
  1027            do_GetService("@mozilla.org/observer-service;1");
  1028   if (os)
  1030     os->AddObserver(this, "memory-pressure", true);
  1031     os->AddObserver(this, "chrome-flush-caches", true);
  1032     os->AddObserver(this, "flush-cache-entry", true);
  1034 // ignore failure of the observer registration.
  1036   return NS_OK;
  1039 static PLDHashOperator
  1040 DropZipReaderCache(const nsACString &aKey, nsJAR* aZip, void*)
  1042   aZip->SetZipReaderCache(nullptr);
  1043   return PL_DHASH_NEXT;
  1046 nsZipReaderCache::~nsZipReaderCache()
  1048   mZips.EnumerateRead(DropZipReaderCache, nullptr);
  1050 #ifdef ZIP_CACHE_HIT_RATE
  1051   printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
  1052          mCacheSize, mZipCacheHits, mZipCacheLookups,
  1053          (float)mZipCacheHits / mZipCacheLookups,
  1054          mZipCacheFlushes, mZipSyncMisses);
  1055 #endif
  1058 NS_IMETHODIMP
  1059 nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult)
  1061   NS_ENSURE_ARG_POINTER(zipFile);
  1062   nsresult rv;
  1063   nsCOMPtr<nsIZipReader> antiLockZipGrip;
  1064   MutexAutoLock lock(mLock);
  1066   nsAutoCString uri;
  1067   rv = zipFile->GetNativePath(uri);
  1068   if (NS_FAILED(rv))
  1069     return rv;
  1071   uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
  1073   *aResult = mZips.Contains(uri);
  1074   return NS_OK;
  1077 NS_IMETHODIMP
  1078 nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
  1080   NS_ENSURE_ARG_POINTER(zipFile);
  1081   nsresult rv;
  1082   nsCOMPtr<nsIZipReader> antiLockZipGrip;
  1083   MutexAutoLock lock(mLock);
  1085 #ifdef ZIP_CACHE_HIT_RATE
  1086   mZipCacheLookups++;
  1087 #endif
  1089   nsAutoCString uri;
  1090   rv = zipFile->GetNativePath(uri);
  1091   if (NS_FAILED(rv)) return rv;
  1093   uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
  1095   nsRefPtr<nsJAR> zip;
  1096   mZips.Get(uri, getter_AddRefs(zip));
  1097   if (zip) {
  1098 #ifdef ZIP_CACHE_HIT_RATE
  1099     mZipCacheHits++;
  1100 #endif
  1101     zip->ClearReleaseTime();
  1102   } else {
  1103     zip = new nsJAR();
  1104     zip->SetZipReaderCache(this);
  1106     rv = zip->Open(zipFile);
  1107     if (NS_FAILED(rv)) {
  1108       return rv;
  1111     MOZ_ASSERT(!mZips.Contains(uri));
  1112     mZips.Put(uri, zip);
  1114   zip.forget(result);
  1115   return rv;
  1118 NS_IMETHODIMP
  1119 nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const nsACString &entry,
  1120                               nsIZipReader* *result)
  1122   NS_ENSURE_ARG_POINTER(zipFile);
  1124   nsCOMPtr<nsIZipReader> outerZipReader;
  1125   nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader));
  1126   NS_ENSURE_SUCCESS(rv, rv);
  1128 #ifdef ZIP_CACHE_HIT_RATE
  1129   mZipCacheLookups++;
  1130 #endif
  1132   nsAutoCString uri;
  1133   rv = zipFile->GetNativePath(uri);
  1134   if (NS_FAILED(rv)) return rv;
  1136   uri.Insert(NS_LITERAL_CSTRING("jar:"), 0);
  1137   uri.AppendLiteral("!/");
  1138   uri.Append(entry);
  1140   nsRefPtr<nsJAR> zip;
  1141   mZips.Get(uri, getter_AddRefs(zip));
  1142   if (zip) {
  1143 #ifdef ZIP_CACHE_HIT_RATE
  1144     mZipCacheHits++;
  1145 #endif
  1146     zip->ClearReleaseTime();
  1147   } else {
  1148     zip = new nsJAR();
  1149     zip->SetZipReaderCache(this);
  1151     rv = zip->OpenInner(outerZipReader, entry);
  1152     if (NS_FAILED(rv)) {
  1153       return rv;
  1156     MOZ_ASSERT(!mZips.Contains(uri));
  1157     mZips.Put(uri, zip);
  1159   zip.forget(result);
  1160   return rv;
  1163 static PLDHashOperator
  1164 FindOldestZip(const nsACString &aKey, nsJAR* aZip, void* aClosure)
  1166   nsJAR** oldestPtr = static_cast<nsJAR**>(aClosure);
  1167   nsJAR* oldest = *oldestPtr;
  1168   nsJAR* current = aZip;
  1169   PRIntervalTime currentReleaseTime = current->GetReleaseTime();
  1170   if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) {
  1171     if (oldest == nullptr ||
  1172         currentReleaseTime < oldest->GetReleaseTime()) {
  1173       *oldestPtr = current;
  1176   return PL_DHASH_NEXT;
  1179 struct ZipFindData {nsJAR* zip; bool found;};
  1181 static PLDHashOperator
  1182 FindZip(const nsACString &aKey, nsJAR* aZip, void* aClosure)
  1184   ZipFindData* find_data = static_cast<ZipFindData*>(aClosure);
  1186   if (find_data->zip == aZip) {
  1187     find_data->found = true;
  1188     return PL_DHASH_STOP;
  1190   return PL_DHASH_NEXT;
  1193 nsresult
  1194 nsZipReaderCache::ReleaseZip(nsJAR* zip)
  1196   nsresult rv;
  1197   MutexAutoLock lock(mLock);
  1199   // It is possible that two thread compete for this zip. The dangerous
  1200   // case is where one thread Releases the zip and discovers that the ref
  1201   // count has gone to one. Before it can call this ReleaseZip method
  1202   // another thread calls our GetZip method. The ref count goes to two. That
  1203   // second thread then Releases the zip and the ref count goes to one. It
  1204   // then tries to enter this ReleaseZip method and blocks while the first
  1205   // thread is still here. The first thread continues and remove the zip from
  1206   // the cache and calls its Release method sending the ref count to 0 and
  1207   // deleting the zip. However, the second thread is still blocked at the
  1208   // start of ReleaseZip, but the 'zip' param now hold a reference to a
  1209   // deleted zip!
  1210   //
  1211   // So, we are going to try safeguarding here by searching our hashtable while
  1212   // locked here for the zip. We return fast if it is not found.
  1214   ZipFindData find_data = {zip, false};
  1215   mZips.EnumerateRead(FindZip, &find_data);
  1216   if (!find_data.found) {
  1217 #ifdef ZIP_CACHE_HIT_RATE
  1218     mZipSyncMisses++;
  1219 #endif
  1220     return NS_OK;
  1223   zip->SetReleaseTime();
  1225   if (mZips.Count() <= mCacheSize)
  1226     return NS_OK;
  1228   nsJAR* oldest = nullptr;
  1229   mZips.EnumerateRead(FindOldestZip, &oldest);
  1231   // Because of the craziness above it is possible that there is no zip that
  1232   // needs removing.
  1233   if (!oldest)
  1234     return NS_OK;
  1236 #ifdef ZIP_CACHE_HIT_RATE
  1237     mZipCacheFlushes++;
  1238 #endif
  1240   // remove from hashtable
  1241   nsAutoCString uri;
  1242   rv = oldest->GetJarPath(uri);
  1243   if (NS_FAILED(rv))
  1244     return rv;
  1246   if (oldest->mOuterZipEntry.IsEmpty()) {
  1247     uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
  1248   } else {
  1249     uri.Insert(NS_LITERAL_CSTRING("jar:"), 0);
  1250     uri.AppendLiteral("!/");
  1251     uri.Append(oldest->mOuterZipEntry);
  1254   // Retrieving and removing the JAR must be done without an extra AddRef
  1255   // and Release, or we'll trigger nsJAR::Release's magic refcount 1 case
  1256   // an extra time and trigger a deadlock.
  1257   nsRefPtr<nsJAR> removed;
  1258   mZips.Remove(uri, getter_AddRefs(removed));
  1259   NS_ASSERTION(removed, "botched");
  1260   NS_ASSERTION(oldest == removed, "removed wrong entry");
  1262   if (removed)
  1263     removed->SetZipReaderCache(nullptr);
  1265   return NS_OK;
  1268 static PLDHashOperator
  1269 FindFlushableZip(const nsACString &aKey, nsRefPtr<nsJAR>& aCurrent, void*)
  1271   if (aCurrent->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) {
  1272     aCurrent->SetZipReaderCache(nullptr);
  1273     return PL_DHASH_REMOVE;
  1275   return PL_DHASH_NEXT;
  1278 NS_IMETHODIMP
  1279 nsZipReaderCache::Observe(nsISupports *aSubject,
  1280                           const char *aTopic,
  1281                           const char16_t *aSomeData)
  1283   if (strcmp(aTopic, "memory-pressure") == 0) {
  1284     MutexAutoLock lock(mLock);
  1285     mZips.Enumerate(FindFlushableZip, nullptr);
  1287   else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
  1288     mZips.EnumerateRead(DropZipReaderCache, nullptr);
  1289     mZips.Clear();
  1291   else if (strcmp(aTopic, "flush-cache-entry") == 0) {
  1292     nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
  1293     if (!file)
  1294       return NS_OK;
  1296     nsAutoCString uri;
  1297     if (NS_FAILED(file->GetNativePath(uri)))
  1298       return NS_OK;
  1300     uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
  1302     MutexAutoLock lock(mLock);
  1304     nsRefPtr<nsJAR> zip;
  1305     mZips.Get(uri, getter_AddRefs(zip));
  1306     if (!zip)
  1307       return NS_OK;
  1309 #ifdef ZIP_CACHE_HIT_RATE
  1310     mZipCacheFlushes++;
  1311 #endif
  1313     zip->SetZipReaderCache(nullptr);
  1315     mZips.Remove(uri);
  1317   return NS_OK;
  1320 ////////////////////////////////////////////////////////////////////////////////

mercurial