LCOV - code coverage report
Current view: top level - netwerk/cache2 - CacheFileIOManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 744 1938 38.4 %
Date: 2017-07-14 16:53:18 Functions: 90 168 53.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "CacheLog.h"
       6             : #include "CacheFileIOManager.h"
       7             : 
       8             : #include "../cache/nsCacheUtils.h"
       9             : #include "CacheHashUtils.h"
      10             : #include "CacheStorageService.h"
      11             : #include "CacheIndex.h"
      12             : #include "CacheFileUtils.h"
      13             : #include "nsThreadUtils.h"
      14             : #include "CacheFile.h"
      15             : #include "CacheObserver.h"
      16             : #include "nsIFile.h"
      17             : #include "CacheFileContextEvictor.h"
      18             : #include "nsITimer.h"
      19             : #include "nsISimpleEnumerator.h"
      20             : #include "nsIDirectoryEnumerator.h"
      21             : #include "nsIObserverService.h"
      22             : #include "nsICacheStorageVisitor.h"
      23             : #include "nsISizeOf.h"
      24             : #include "mozilla/Telemetry.h"
      25             : #include "mozilla/DebugOnly.h"
      26             : #include "mozilla/Services.h"
      27             : #include "nsDirectoryServiceUtils.h"
      28             : #include "nsAppDirectoryServiceDefs.h"
      29             : #include "private/pprio.h"
      30             : #include "mozilla/IntegerPrintfMacros.h"
      31             : #include "mozilla/Preferences.h"
      32             : #include "nsNetUtil.h"
      33             : 
      34             : // include files for ftruncate (or equivalent)
      35             : #if defined(XP_UNIX)
      36             : #include <unistd.h>
      37             : #elif defined(XP_WIN)
      38             : #include <windows.h>
      39             : #undef CreateFile
      40             : #undef CREATE_NEW
      41             : #else
      42             : // XXX add necessary include file for ftruncate (or equivalent)
      43             : #endif
      44             : 
      45             : 
      46             : namespace mozilla {
      47             : namespace net {
      48             : 
      49             : #define kOpenHandlesLimit        128
      50             : #define kMetadataWriteDelay      5000
      51             : #define kRemoveTrashStartDelay   60000 // in milliseconds
      52             : #define kSmartSizeUpdateInterval 60000 // in milliseconds
      53             : 
      54             : #ifdef ANDROID
      55             : const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
      56             : #else
      57             : const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
      58             : #endif
      59             : 
      60             : bool
      61          39 : CacheFileHandle::DispatchRelease()
      62             : {
      63          39 :   if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
      64          39 :     return false;
      65             :   }
      66             : 
      67           0 :   nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
      68           0 :   if (!ioTarget) {
      69           0 :     return false;
      70             :   }
      71             : 
      72           0 :   nsresult rv = ioTarget->Dispatch(
      73           0 :     NewNonOwningRunnableMethod(
      74             :       "net::CacheFileHandle::Release", this, &CacheFileHandle::Release),
      75           0 :     nsIEventTarget::DISPATCH_NORMAL);
      76           0 :   if (NS_FAILED(rv)) {
      77           0 :     return false;
      78             :   }
      79             : 
      80           0 :   return true;
      81             : }
      82             : 
      83          49 : NS_IMPL_ADDREF(CacheFileHandle)
      84             : NS_IMETHODIMP_(MozExternalRefCountType)
      85          39 : CacheFileHandle::Release()
      86             : {
      87          39 :   nsrefcnt count = mRefCnt - 1;
      88          39 :   if (DispatchRelease()) {
      89             :     // Redispatched to the IO thread.
      90           0 :     return count;
      91             :   }
      92             : 
      93          39 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
      94             : 
      95          39 :   LOG(("CacheFileHandle::Release() [this=%p, refcnt=%" PRIuPTR "]", this, mRefCnt.get()));
      96          39 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
      97          39 :   count = --mRefCnt;
      98          39 :   NS_LOG_RELEASE(this, count, "CacheFileHandle");
      99             : 
     100          39 :   if (0 == count) {
     101           0 :     mRefCnt = 1;
     102           0 :     delete (this);
     103           0 :     return 0;
     104             :   }
     105             : 
     106          39 :   return count;
     107             : }
     108             : 
     109           0 : NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
     110           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     111           0 : NS_INTERFACE_MAP_END_THREADSAFE
     112             : 
     113           5 : CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority, PinningStatus aPinning)
     114             :   : mHash(aHash)
     115             :   , mIsDoomed(false)
     116             :   , mClosed(false)
     117             :   , mPriority(aPriority)
     118             :   , mSpecialFile(false)
     119             :   , mInvalid(false)
     120             :   , mFileExists(false)
     121             :   , mDoomWhenFoundPinned(false)
     122             :   , mDoomWhenFoundNonPinned(false)
     123             :   , mKilled(false)
     124             :   , mPinning(aPinning)
     125             :   , mFileSize(-1)
     126           5 :   , mFD(nullptr)
     127             : {
     128             :   // If we initialize mDoomed in the initialization list, that initialization is
     129             :   // not guaranteeded to be atomic.  Whereas this assignment here is guaranteed
     130             :   // to be atomic.  TSan will see this (atomic) assignment and be satisfied
     131             :   // that cross-thread accesses to mIsDoomed are properly synchronized.
     132           5 :   mIsDoomed = false;
     133           5 :   LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
     134             :        , this, LOGSHA1(aHash)));
     135           5 : }
     136             : 
     137           0 : CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority, PinningStatus aPinning)
     138             :   : mHash(nullptr)
     139             :   , mIsDoomed(false)
     140             :   , mClosed(false)
     141             :   , mPriority(aPriority)
     142             :   , mSpecialFile(true)
     143             :   , mInvalid(false)
     144             :   , mFileExists(false)
     145             :   , mDoomWhenFoundPinned(false)
     146             :   , mDoomWhenFoundNonPinned(false)
     147             :   , mKilled(false)
     148             :   , mPinning(aPinning)
     149             :   , mFileSize(-1)
     150             :   , mFD(nullptr)
     151           0 :   , mKey(aKey)
     152             : {
     153             :   // See comment above about the initialization of mIsDoomed.
     154           0 :   mIsDoomed = false;
     155           0 :   LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
     156             :        PromiseFlatCString(aKey).get()));
     157           0 : }
     158             : 
     159           0 : CacheFileHandle::~CacheFileHandle()
     160             : {
     161           0 :   LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
     162             : 
     163           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     164             : 
     165           0 :   RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
     166           0 :   if (!IsClosed() && ioMan) {
     167           0 :     ioMan->CloseHandleInternal(this);
     168             :   }
     169           0 : }
     170             : 
     171             : void
     172           0 : CacheFileHandle::Log()
     173             : {
     174           0 :   nsAutoCString leafName;
     175           0 :   if (mFile) {
     176           0 :     mFile->GetNativeLeafName(leafName);
     177             :   }
     178             : 
     179           0 :   if (mSpecialFile) {
     180           0 :     LOG(("CacheFileHandle::Log() - special file [this=%p, "
     181             :          "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
     182             :          "pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64 ", leafName=%s, key=%s]",
     183             :          this,
     184             :          bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
     185             :          static_cast<uint32_t>(mPinning), bool(mFileExists), mFileSize, leafName.get(),
     186             :          mKey.get()));
     187             :   } else {
     188           0 :     LOG(("CacheFileHandle::Log() - entry file [this=%p, hash=%08x%08x%08x%08x%08x, "
     189             :          "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
     190             :          "pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64 ", leafName=%s, key=%s]",
     191             :          this, LOGSHA1(mHash),
     192             :          bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
     193             :          static_cast<uint32_t>(mPinning), bool(mFileExists), mFileSize, leafName.get(),
     194             :          mKey.get()));
     195             :   }
     196           0 : }
     197             : 
     198             : uint32_t
     199          19 : CacheFileHandle::FileSizeInK() const
     200             : {
     201          19 :   MOZ_ASSERT(mFileSize != -1);
     202          19 :   uint64_t size64 = mFileSize;
     203             : 
     204          19 :   size64 += 0x3FF;
     205          19 :   size64 >>= 10;
     206             : 
     207             :   uint32_t size;
     208          19 :   if (size64 >> 32) {
     209             :     NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
     210           0 :                "truncating to PR_UINT32_MAX");
     211           0 :     size = PR_UINT32_MAX;
     212             :   } else {
     213          19 :     size = static_cast<uint32_t>(size64);
     214             :   }
     215             : 
     216          19 :   return size;
     217             : }
     218             : 
     219             : bool
     220           5 : CacheFileHandle::SetPinned(bool aPinned)
     221             : {
     222           5 :   LOG(("CacheFileHandle::SetPinned [this=%p, pinned=%d]", this, aPinned));
     223             : 
     224           5 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     225             : 
     226           5 :   mPinning = aPinned
     227           5 :     ? PinningStatus::PINNED
     228             :     : PinningStatus::NON_PINNED;
     229             : 
     230          10 :   if ((MOZ_UNLIKELY(mDoomWhenFoundPinned) && aPinned) ||
     231           5 :       (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && !aPinned)) {
     232             : 
     233           0 :     LOG(("  dooming, when: pinned=%d, non-pinned=%d, found: pinned=%d",
     234             :       bool(mDoomWhenFoundPinned), bool(mDoomWhenFoundNonPinned), aPinned));
     235             : 
     236           0 :     mDoomWhenFoundPinned = false;
     237           0 :     mDoomWhenFoundNonPinned = false;
     238             : 
     239           0 :     return false;
     240             :   }
     241             : 
     242           5 :   return true;
     243             : }
     244             : 
     245             : // Memory reporting
     246             : 
     247             : size_t
     248           0 : CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     249             : {
     250           0 :   size_t n = 0;
     251           0 :   nsCOMPtr<nsISizeOf> sizeOf;
     252             : 
     253           0 :   sizeOf = do_QueryInterface(mFile);
     254           0 :   if (sizeOf) {
     255           0 :     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
     256             :   }
     257             : 
     258           0 :   n += mallocSizeOf(mFD);
     259           0 :   n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
     260           0 :   return n;
     261             : }
     262             : 
     263             : size_t
     264           0 : CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     265             : {
     266           0 :   return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
     267             : }
     268             : 
     269             : /******************************************************************************
     270             :  *  CacheFileHandles::HandleHashKey
     271             :  *****************************************************************************/
     272             : 
     273             : void
     274           5 : CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
     275             : {
     276           5 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     277             : 
     278           5 :   mHandles.InsertElementAt(0, aHandle);
     279           5 : }
     280             : 
     281             : void
     282           0 : CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
     283             : {
     284           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     285             : 
     286           0 :   DebugOnly<bool> found;
     287           0 :   found = mHandles.RemoveElement(aHandle);
     288           0 :   MOZ_ASSERT(found);
     289           0 : }
     290             : 
     291             : already_AddRefed<CacheFileHandle>
     292           0 : CacheFileHandles::HandleHashKey::GetNewestHandle()
     293             : {
     294           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     295             : 
     296           0 :   RefPtr<CacheFileHandle> handle;
     297           0 :   if (mHandles.Length()) {
     298           0 :     handle = mHandles[0];
     299             :   }
     300             : 
     301           0 :   return handle.forget();
     302             : }
     303             : 
     304             : void
     305           0 : CacheFileHandles::HandleHashKey::GetHandles(nsTArray<RefPtr<CacheFileHandle> > &aResult)
     306             : {
     307           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     308             : 
     309           0 :   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
     310           0 :     CacheFileHandle* handle = mHandles[i];
     311           0 :     aResult.AppendElement(handle);
     312             :   }
     313           0 : }
     314             : 
     315             : #ifdef DEBUG
     316             : 
     317             : void
     318           5 : CacheFileHandles::HandleHashKey::AssertHandlesState()
     319             : {
     320           5 :   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
     321           0 :     CacheFileHandle* handle = mHandles[i];
     322           0 :     MOZ_ASSERT(handle->IsDoomed());
     323             :   }
     324           5 : }
     325             : 
     326             : #endif
     327             : 
     328             : size_t
     329           0 : CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     330             : {
     331           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
     332             : 
     333           0 :   size_t n = 0;
     334           0 :   n += mallocSizeOf(mHash.get());
     335           0 :   for (uint32_t i = 0; i < mHandles.Length(); ++i) {
     336           0 :     n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
     337             :   }
     338             : 
     339           0 :   return n;
     340             : }
     341             : 
     342             : /******************************************************************************
     343             :  *  CacheFileHandles
     344             :  *****************************************************************************/
     345             : 
     346           1 : CacheFileHandles::CacheFileHandles()
     347             : {
     348           1 :   LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
     349           1 :   MOZ_COUNT_CTOR(CacheFileHandles);
     350           1 : }
     351             : 
     352           0 : CacheFileHandles::~CacheFileHandles()
     353             : {
     354           0 :   LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
     355           0 :   MOZ_COUNT_DTOR(CacheFileHandles);
     356           0 : }
     357             : 
     358             : nsresult
     359           6 : CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
     360             :                             CacheFileHandle **_retval)
     361             : {
     362           6 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     363           6 :   MOZ_ASSERT(aHash);
     364             : 
     365             : #ifdef DEBUG_HANDLES
     366             :   LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
     367             :        LOGSHA1(aHash)));
     368             : #endif
     369             : 
     370             :   // find hash entry for key
     371           6 :   HandleHashKey *entry = mTable.GetEntry(*aHash);
     372           6 :   if (!entry) {
     373           6 :     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
     374             :          "no handle entries found", LOGSHA1(aHash)));
     375           6 :     return NS_ERROR_NOT_AVAILABLE;
     376             :   }
     377             : 
     378             : #ifdef DEBUG_HANDLES
     379             :   Log(entry);
     380             : #endif
     381             : 
     382             :   // Check if the entry is doomed
     383           0 :   RefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
     384           0 :   if (!handle) {
     385           0 :     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
     386             :          "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
     387           0 :     return NS_ERROR_NOT_AVAILABLE;
     388             :   }
     389             : 
     390           0 :   if (handle->IsDoomed()) {
     391           0 :     LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
     392             :          "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
     393           0 :     return NS_ERROR_NOT_AVAILABLE;
     394             :   }
     395             : 
     396           0 :   LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
     397             :        "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
     398             : 
     399           0 :   handle.forget(_retval);
     400           0 :   return NS_OK;
     401             : }
     402             : 
     403             : 
     404             : nsresult
     405           5 : CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
     406             :                             bool aPriority, CacheFileHandle::PinningStatus aPinning,
     407             :                             CacheFileHandle **_retval)
     408             : {
     409           5 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     410           5 :   MOZ_ASSERT(aHash);
     411             : 
     412             : #ifdef DEBUG_HANDLES
     413             :   LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
     414             : #endif
     415             : 
     416             :   // find hash entry for key
     417           5 :   HandleHashKey *entry = mTable.PutEntry(*aHash);
     418             : 
     419             : #ifdef DEBUG_HANDLES
     420             :   Log(entry);
     421             : #endif
     422             : 
     423             : #ifdef DEBUG
     424           5 :   entry->AssertHandlesState();
     425             : #endif
     426             : 
     427          15 :   RefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority, aPinning);
     428           5 :   entry->AddHandle(handle);
     429             : 
     430           5 :   LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
     431             :        "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
     432             : 
     433           5 :   handle.forget(_retval);
     434          10 :   return NS_OK;
     435             : }
     436             : 
     437             : void
     438           0 : CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
     439             : {
     440           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     441           0 :   MOZ_ASSERT(aHandle);
     442             : 
     443           0 :   if (!aHandle) {
     444           0 :     return;
     445             :   }
     446             : 
     447             : #ifdef DEBUG_HANDLES
     448             :   LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
     449             :        , aHandle, LOGSHA1(aHandle->Hash())));
     450             : #endif
     451             : 
     452             :   // find hash entry for key
     453           0 :   HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
     454           0 :   if (!entry) {
     455           0 :     MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
     456             :       "Should find entry when removing a handle before shutdown");
     457             : 
     458           0 :     LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
     459             :          "no entries found", LOGSHA1(aHandle->Hash())));
     460           0 :     return;
     461             :   }
     462             : 
     463             : #ifdef DEBUG_HANDLES
     464             :   Log(entry);
     465             : #endif
     466             : 
     467           0 :   LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
     468             :        "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
     469           0 :   entry->RemoveHandle(aHandle);
     470             : 
     471           0 :   if (entry->IsEmpty()) {
     472           0 :     LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
     473             :          "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
     474           0 :     mTable.RemoveEntry(entry);
     475             :   }
     476             : }
     477             : 
     478             : void
     479           0 : CacheFileHandles::GetAllHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval)
     480             : {
     481           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     482           0 :   for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
     483           0 :     iter.Get()->GetHandles(*_retval);
     484             :   }
     485           0 : }
     486             : 
     487             : void
     488           0 : CacheFileHandles::GetActiveHandles(
     489             :   nsTArray<RefPtr<CacheFileHandle> > *_retval)
     490             : {
     491           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     492           0 :   for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
     493           0 :     RefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle();
     494           0 :     MOZ_ASSERT(handle);
     495             : 
     496           0 :     if (!handle->IsDoomed()) {
     497           0 :       _retval->AppendElement(handle);
     498             :     }
     499             :   }
     500           0 : }
     501             : 
     502             : void
     503           0 : CacheFileHandles::ClearAll()
     504             : {
     505           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
     506           0 :   mTable.Clear();
     507           0 : }
     508             : 
     509             : uint32_t
     510           0 : CacheFileHandles::HandleCount()
     511             : {
     512           0 :   return mTable.Count();
     513             : }
     514             : 
     515             : #ifdef DEBUG_HANDLES
     516             : void
     517             : CacheFileHandles::Log(CacheFileHandlesEntry *entry)
     518             : {
     519             :   LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
     520             : 
     521             :   nsTArray<RefPtr<CacheFileHandle> > array;
     522             :   aEntry->GetHandles(array);
     523             : 
     524             :   for (uint32_t i = 0; i < array.Length(); ++i) {
     525             :     CacheFileHandle *handle = array[i];
     526             :     handle->Log();
     527             :   }
     528             : 
     529             :   LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
     530             : }
     531             : #endif
     532             : 
     533             : // Memory reporting
     534             : 
     535             : size_t
     536           0 : CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     537             : {
     538           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
     539             : 
     540           0 :   return mTable.SizeOfExcludingThis(mallocSizeOf);
     541             : }
     542             : 
     543             : // Events
     544             : 
     545             : class ShutdownEvent : public Runnable {
     546             : public:
     547           0 :   ShutdownEvent()
     548           0 :     : Runnable("net::ShutdownEvent")
     549             :     , mMonitor("ShutdownEvent.mMonitor")
     550           0 :     , mNotified(false)
     551             :   {
     552           0 :   }
     553             : 
     554             : protected:
     555           0 :   ~ShutdownEvent()
     556           0 :   {
     557           0 :   }
     558             : 
     559             : public:
     560           0 :   NS_IMETHOD Run() override
     561             :   {
     562           0 :     MonitorAutoLock mon(mMonitor);
     563             : 
     564           0 :     CacheFileIOManager::gInstance->ShutdownInternal();
     565             : 
     566           0 :     mNotified = true;
     567           0 :     mon.Notify();
     568             : 
     569           0 :     return NS_OK;
     570             :   }
     571             : 
     572           0 :   void PostAndWait()
     573             :   {
     574           0 :     MonitorAutoLock mon(mMonitor);
     575             : 
     576           0 :     DebugOnly<nsresult> rv;
     577           0 :     rv = CacheFileIOManager::gInstance->mIOThread->Dispatch(
     578           0 :       this, CacheIOThread::WRITE); // When writes and closing of handles is done
     579           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     580             : 
     581           0 :     PRIntervalTime const waitTime = PR_MillisecondsToInterval(1000);
     582           0 :     while (!mNotified) {
     583           0 :       mon.Wait(waitTime);
     584           0 :       if (!mNotified) {
     585             :         // If there is any IO blocking on the IO thread, this will
     586             :         // try to cancel it.  Returns no later than after two seconds.
     587           0 :         MonitorAutoUnlock unmon(mMonitor); // Prevent delays
     588           0 :         CacheFileIOManager::gInstance->mIOThread->CancelBlockingIO();
     589             :       }
     590             :     }
     591           0 :   }
     592             : 
     593             : protected:
     594             :   mozilla::Monitor mMonitor;
     595             :   bool             mNotified;
     596             : };
     597             : 
     598             : // Class responsible for reporting IO performance stats
     599             : class IOPerfReportEvent {
     600             : public:
     601          19 :   explicit IOPerfReportEvent(CacheFileUtils::CachePerfStats::EDataType aType)
     602          19 :     : mType(aType)
     603          19 :     , mEventCounter(0)
     604             :   {
     605          19 :   }
     606             : 
     607          16 :   void Start(CacheIOThread *aIOThread)
     608             :   {
     609          16 :     mStartTime = TimeStamp::Now();
     610          16 :     mEventCounter = aIOThread->EventCounter();
     611          16 :   }
     612             : 
     613          16 :   void Report(CacheIOThread *aIOThread)
     614             :   {
     615          16 :     if (mStartTime.IsNull()) {
     616           0 :       return;
     617             :     }
     618             : 
     619             :     // Single IO operations can take less than 1ms. So we use microseconds to
     620             :     // keep a good resolution of data.
     621          16 :     uint32_t duration = (TimeStamp::Now() - mStartTime).ToMicroseconds();
     622             : 
     623             :     // This is a simple prefiltering of values that might differ a lot from the
     624             :     // average value. Do not add the value to the filtered stats when the event
     625             :     // had to wait in a long queue.
     626          16 :     uint32_t eventCounter = aIOThread->EventCounter();
     627          16 :     bool shortOnly = eventCounter - mEventCounter < 5 ? false : true;
     628             : 
     629          16 :     CacheFileUtils::CachePerfStats::AddValue(mType, duration, shortOnly);
     630             :   }
     631             : 
     632             : protected:
     633             :   CacheFileUtils::CachePerfStats::EDataType mType;
     634             :   TimeStamp                             mStartTime;
     635             :   uint32_t                              mEventCounter;
     636             : };
     637             : 
     638             : class OpenFileEvent : public Runnable
     639             :                     , public IOPerfReportEvent {
     640             : public:
     641           8 :   OpenFileEvent(const nsACString &aKey, uint32_t aFlags,
     642             :                 CacheFileIOListener *aCallback)
     643           8 :     : Runnable("net::OpenFileEvent")
     644             :     , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_OPEN)
     645             :     , mFlags(aFlags)
     646             :     , mCallback(aCallback)
     647           8 :     , mKey(aKey)
     648             :   {
     649           8 :     mIOMan = CacheFileIOManager::gInstance;
     650           8 :     if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
     651           5 :       Start(mIOMan->mIOThread);
     652             :     }
     653           8 :   }
     654             : 
     655             : protected:
     656          16 :   ~OpenFileEvent()
     657           8 :   {
     658          24 :   }
     659             : 
     660             : public:
     661           8 :   NS_IMETHOD Run() override
     662             :   {
     663           8 :     nsresult rv = NS_OK;
     664             : 
     665           8 :     if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
     666           5 :       SHA1Sum sum;
     667           5 :       sum.update(mKey.BeginReading(), mKey.Length());
     668           5 :       sum.finish(mHash);
     669             :     }
     670             : 
     671           8 :     if (!mIOMan) {
     672           0 :       rv = NS_ERROR_NOT_INITIALIZED;
     673             :     } else {
     674           8 :       if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
     675           3 :         rv = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
     676           6 :                                              getter_AddRefs(mHandle));
     677             :       } else {
     678           5 :         rv = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
     679          10 :                                       getter_AddRefs(mHandle));
     680           5 :         if (NS_SUCCEEDED(rv)) {
     681           5 :           Report(mIOMan->mIOThread);
     682             :         }
     683             :       }
     684           8 :       mIOMan = nullptr;
     685           8 :       if (mHandle) {
     686           5 :         if (mHandle->Key().IsEmpty()) {
     687           5 :           mHandle->Key() = mKey;
     688             :         }
     689             :       }
     690             :     }
     691             : 
     692           8 :     mCallback->OnFileOpened(mHandle, rv);
     693           8 :     return NS_OK;
     694             :   }
     695             : 
     696             : protected:
     697             :   SHA1Sum::Hash                 mHash;
     698             :   uint32_t                      mFlags;
     699             :   nsCOMPtr<CacheFileIOListener> mCallback;
     700             :   RefPtr<CacheFileIOManager>    mIOMan;
     701             :   RefPtr<CacheFileHandle>       mHandle;
     702             :   nsCString                     mKey;
     703             : };
     704             : 
     705             : class ReadEvent : public Runnable
     706             :                 , public IOPerfReportEvent {
     707             : public:
     708           5 :   ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
     709             :             int32_t aCount, CacheFileIOListener *aCallback)
     710           5 :     : Runnable("net::ReadEvent")
     711             :     , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_READ)
     712             :     , mHandle(aHandle)
     713             :     , mOffset(aOffset)
     714             :     , mBuf(aBuf)
     715             :     , mCount(aCount)
     716           5 :     , mCallback(aCallback)
     717             :   {
     718           5 :     if (!mHandle->IsSpecialFile()) {
     719           5 :       Start(CacheFileIOManager::gInstance->mIOThread);
     720             :     }
     721           5 :   }
     722             : 
     723             : protected:
     724          10 :   ~ReadEvent()
     725           5 :   {
     726          15 :   }
     727             : 
     728             : public:
     729           5 :   NS_IMETHOD Run() override
     730             :   {
     731             :     nsresult rv;
     732             : 
     733           5 :     if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
     734           0 :       rv = NS_ERROR_NOT_INITIALIZED;
     735             :     } else {
     736           5 :       rv = CacheFileIOManager::gInstance->ReadInternal(
     737           5 :         mHandle, mOffset, mBuf, mCount);
     738           5 :       if (NS_SUCCEEDED(rv)) {
     739           5 :         Report(CacheFileIOManager::gInstance->mIOThread);
     740             :       }
     741             :     }
     742             : 
     743           5 :     mCallback->OnDataRead(mHandle, mBuf, rv);
     744           5 :     return NS_OK;
     745             :   }
     746             : 
     747             : protected:
     748             :   RefPtr<CacheFileHandle>       mHandle;
     749             :   int64_t                       mOffset;
     750             :   char                         *mBuf;
     751             :   int32_t                       mCount;
     752             :   nsCOMPtr<CacheFileIOListener> mCallback;
     753             : };
     754             : 
     755             : class WriteEvent : public Runnable
     756             :                  , public IOPerfReportEvent {
     757             : public:
     758           6 :   WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
     759             :              int32_t aCount, bool aValidate, bool aTruncate,
     760             :              CacheFileIOListener *aCallback)
     761           6 :     : Runnable("net::WriteEvent")
     762             :     , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_WRITE)
     763             :     , mHandle(aHandle)
     764             :     , mOffset(aOffset)
     765             :     , mBuf(aBuf)
     766             :     , mCount(aCount)
     767             :     , mValidate(aValidate)
     768             :     , mTruncate(aTruncate)
     769           6 :     , mCallback(aCallback)
     770             :   {
     771           6 :     if (!mHandle->IsSpecialFile()) {
     772           6 :       Start(CacheFileIOManager::gInstance->mIOThread);
     773             :     }
     774           6 :   }
     775             : 
     776             : protected:
     777          12 :   ~WriteEvent()
     778          12 :   {
     779           6 :     if (!mCallback && mBuf) {
     780           0 :       free(const_cast<char *>(mBuf));
     781             :     }
     782          18 :   }
     783             : 
     784             : public:
     785           6 :   NS_IMETHOD Run() override
     786             :   {
     787             :     nsresult rv;
     788             : 
     789           6 :     if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
     790             :       // We usually get here only after the internal shutdown
     791             :       // (i.e. mShuttingDown == true).  Pretend write has succeeded
     792             :       // to avoid any past-shutdown file dooming.
     793           0 :       rv = (CacheObserver::IsPastShutdownIOLag() ||
     794           0 :             CacheFileIOManager::gInstance->mShuttingDown)
     795           0 :         ? NS_OK
     796             :         : NS_ERROR_NOT_INITIALIZED;
     797             :     } else {
     798          12 :       rv = CacheFileIOManager::gInstance->WriteInternal(
     799          18 :           mHandle, mOffset, mBuf, mCount, mValidate, mTruncate);
     800           6 :       if (NS_SUCCEEDED(rv)) {
     801           6 :         Report(CacheFileIOManager::gInstance->mIOThread);
     802             :       }
     803           6 :       if (NS_FAILED(rv) && !mCallback) {
     804             :         // No listener is going to handle the error, doom the file
     805           0 :         CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
     806             :       }
     807             :     }
     808           6 :     if (mCallback) {
     809           6 :       mCallback->OnDataWritten(mHandle, mBuf, rv);
     810             :     } else {
     811           0 :       free(const_cast<char *>(mBuf));
     812           0 :       mBuf = nullptr;
     813             :     }
     814             : 
     815           6 :     return NS_OK;
     816             :   }
     817             : 
     818             : protected:
     819             :   RefPtr<CacheFileHandle>       mHandle;
     820             :   int64_t                       mOffset;
     821             :   const char                   *mBuf;
     822             :   int32_t                       mCount;
     823             :   bool                          mValidate : 1;
     824             :   bool                          mTruncate : 1;
     825             :   nsCOMPtr<CacheFileIOListener> mCallback;
     826             : };
     827             : 
     828             : class DoomFileEvent : public Runnable {
     829             : public:
     830           0 :   DoomFileEvent(CacheFileHandle* aHandle, CacheFileIOListener* aCallback)
     831           0 :     : Runnable("net::DoomFileEvent")
     832             :     , mCallback(aCallback)
     833           0 :     , mHandle(aHandle)
     834             :   {
     835           0 :   }
     836             : 
     837             : protected:
     838           0 :   ~DoomFileEvent()
     839           0 :   {
     840           0 :   }
     841             : 
     842             : public:
     843           0 :   NS_IMETHOD Run() override
     844             :   {
     845             :     nsresult rv;
     846             : 
     847           0 :     if (mHandle->IsClosed()) {
     848           0 :       rv = NS_ERROR_NOT_INITIALIZED;
     849             :     } else {
     850           0 :       rv = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
     851             :     }
     852             : 
     853           0 :     if (mCallback) {
     854           0 :       mCallback->OnFileDoomed(mHandle, rv);
     855             :     }
     856             : 
     857           0 :     return NS_OK;
     858             :   }
     859             : 
     860             : protected:
     861             :   nsCOMPtr<CacheFileIOListener>              mCallback;
     862             :   nsCOMPtr<nsIEventTarget>                   mTarget;
     863             :   RefPtr<CacheFileHandle>                    mHandle;
     864             : };
     865             : 
     866             : class DoomFileByKeyEvent : public Runnable {
     867             : public:
     868           1 :   DoomFileByKeyEvent(const nsACString& aKey, CacheFileIOListener* aCallback)
     869           1 :     : Runnable("net::DoomFileByKeyEvent")
     870           1 :     , mCallback(aCallback)
     871             :   {
     872           1 :     SHA1Sum sum;
     873           1 :     sum.update(aKey.BeginReading(), aKey.Length());
     874           1 :     sum.finish(mHash);
     875             : 
     876           1 :     mIOMan = CacheFileIOManager::gInstance;
     877           1 :   }
     878             : 
     879             : protected:
     880           2 :   ~DoomFileByKeyEvent()
     881           1 :   {
     882           3 :   }
     883             : 
     884             : public:
     885           1 :   NS_IMETHOD Run() override
     886             :   {
     887             :     nsresult rv;
     888             : 
     889           1 :     if (!mIOMan) {
     890           0 :       rv = NS_ERROR_NOT_INITIALIZED;
     891             :     } else {
     892           1 :       rv = mIOMan->DoomFileByKeyInternal(&mHash);
     893           1 :       mIOMan = nullptr;
     894             :     }
     895             : 
     896           1 :     if (mCallback) {
     897           1 :       mCallback->OnFileDoomed(nullptr, rv);
     898             :     }
     899             : 
     900           1 :     return NS_OK;
     901             :   }
     902             : 
     903             : protected:
     904             :   SHA1Sum::Hash                 mHash;
     905             :   nsCOMPtr<CacheFileIOListener> mCallback;
     906             :   RefPtr<CacheFileIOManager>    mIOMan;
     907             : };
     908             : 
     909             : class ReleaseNSPRHandleEvent : public Runnable {
     910             : public:
     911           4 :   explicit ReleaseNSPRHandleEvent(CacheFileHandle* aHandle)
     912           4 :     : Runnable("net::ReleaseNSPRHandleEvent")
     913           4 :     , mHandle(aHandle)
     914             :   {
     915           4 :   }
     916             : 
     917             : protected:
     918           8 :   ~ReleaseNSPRHandleEvent()
     919           4 :   {
     920          12 :   }
     921             : 
     922             : public:
     923           4 :   NS_IMETHOD Run() override
     924             :   {
     925           4 :     if (!mHandle->IsClosed()) {
     926           4 :       CacheFileIOManager::gInstance->MaybeReleaseNSPRHandleInternal(mHandle);
     927             :     }
     928             : 
     929           4 :     return NS_OK;
     930             :   }
     931             : 
     932             : protected:
     933             :   RefPtr<CacheFileHandle>       mHandle;
     934             : };
     935             : 
     936             : class TruncateSeekSetEOFEvent : public Runnable {
     937             : public:
     938           0 :   TruncateSeekSetEOFEvent(CacheFileHandle* aHandle,
     939             :                           int64_t aTruncatePos,
     940             :                           int64_t aEOFPos,
     941             :                           CacheFileIOListener* aCallback)
     942           0 :     : Runnable("net::TruncateSeekSetEOFEvent")
     943             :     , mHandle(aHandle)
     944             :     , mTruncatePos(aTruncatePos)
     945             :     , mEOFPos(aEOFPos)
     946           0 :     , mCallback(aCallback)
     947             :   {
     948           0 :   }
     949             : 
     950             : protected:
     951           0 :   ~TruncateSeekSetEOFEvent()
     952           0 :   {
     953           0 :   }
     954             : 
     955             : public:
     956           0 :   NS_IMETHOD Run() override
     957             :   {
     958             :     nsresult rv;
     959             : 
     960           0 :     if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
     961           0 :       rv = NS_ERROR_NOT_INITIALIZED;
     962             :     } else {
     963           0 :       rv = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
     964           0 :         mHandle, mTruncatePos, mEOFPos);
     965             :     }
     966             : 
     967           0 :     if (mCallback) {
     968           0 :       mCallback->OnEOFSet(mHandle, rv);
     969             :     }
     970             : 
     971           0 :     return NS_OK;
     972             :   }
     973             : 
     974             : protected:
     975             :   RefPtr<CacheFileHandle>       mHandle;
     976             :   int64_t                       mTruncatePos;
     977             :   int64_t                       mEOFPos;
     978             :   nsCOMPtr<CacheFileIOListener> mCallback;
     979             : };
     980             : 
     981             : class RenameFileEvent : public Runnable {
     982             : public:
     983           0 :   RenameFileEvent(CacheFileHandle* aHandle,
     984             :                   const nsACString& aNewName,
     985             :                   CacheFileIOListener* aCallback)
     986           0 :     : Runnable("net::RenameFileEvent")
     987             :     , mHandle(aHandle)
     988             :     , mNewName(aNewName)
     989           0 :     , mCallback(aCallback)
     990             :   {
     991           0 :   }
     992             : 
     993             : protected:
     994           0 :   ~RenameFileEvent()
     995           0 :   {
     996           0 :   }
     997             : 
     998             : public:
     999           0 :   NS_IMETHOD Run() override
    1000             :   {
    1001             :     nsresult rv;
    1002             : 
    1003           0 :     if (mHandle->IsClosed()) {
    1004           0 :       rv = NS_ERROR_NOT_INITIALIZED;
    1005             :     } else {
    1006           0 :       rv = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
    1007           0 :                                                              mNewName);
    1008             :     }
    1009             : 
    1010           0 :     if (mCallback) {
    1011           0 :       mCallback->OnFileRenamed(mHandle, rv);
    1012             :     }
    1013             : 
    1014           0 :     return NS_OK;
    1015             :   }
    1016             : 
    1017             : protected:
    1018             :   RefPtr<CacheFileHandle>       mHandle;
    1019             :   nsCString                     mNewName;
    1020             :   nsCOMPtr<CacheFileIOListener> mCallback;
    1021             : };
    1022             : 
    1023             : class InitIndexEntryEvent : public Runnable {
    1024             : public:
    1025           5 :   InitIndexEntryEvent(CacheFileHandle* aHandle,
    1026             :                       OriginAttrsHash aOriginAttrsHash,
    1027             :                       bool aAnonymous,
    1028             :                       bool aPinning)
    1029           5 :     : Runnable("net::InitIndexEntryEvent")
    1030             :     , mHandle(aHandle)
    1031             :     , mOriginAttrsHash(aOriginAttrsHash)
    1032             :     , mAnonymous(aAnonymous)
    1033           5 :     , mPinning(aPinning)
    1034             :   {
    1035           5 :   }
    1036             : 
    1037             : protected:
    1038          10 :   ~InitIndexEntryEvent()
    1039           5 :   {
    1040          15 :   }
    1041             : 
    1042             : public:
    1043           5 :   NS_IMETHOD Run() override
    1044             :   {
    1045           5 :     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
    1046           0 :       return NS_OK;
    1047             :     }
    1048             : 
    1049           5 :     CacheIndex::InitEntry(mHandle->Hash(), mOriginAttrsHash, mAnonymous,
    1050          10 :                           mPinning);
    1051             : 
    1052             :     // We cannot set the filesize before we init the entry. If we're opening
    1053             :     // an existing entry file, frecency and expiration time will be set after
    1054             :     // parsing the entry file, but we must set the filesize here since nobody is
    1055             :     // going to set it if there is no write to the file.
    1056           5 :     uint32_t sizeInK = mHandle->FileSizeInK();
    1057           5 :     CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
    1058           5 :                             nullptr, &sizeInK);
    1059             : 
    1060           5 :     return NS_OK;
    1061             :   }
    1062             : 
    1063             : protected:
    1064             :   RefPtr<CacheFileHandle> mHandle;
    1065             :   OriginAttrsHash         mOriginAttrsHash;
    1066             :   bool                    mAnonymous;
    1067             :   bool                    mPinning;
    1068             : };
    1069             : 
    1070             : class UpdateIndexEntryEvent : public Runnable {
    1071             : public:
    1072          14 :   UpdateIndexEntryEvent(CacheFileHandle* aHandle,
    1073             :                         const uint32_t* aFrecency,
    1074             :                         const uint32_t* aExpirationTime,
    1075             :                         const bool* aHasAltData,
    1076             :                         const uint16_t* aOnStartTime,
    1077             :                         const uint16_t* aOnStopTime)
    1078          14 :     : Runnable("net::UpdateIndexEntryEvent")
    1079             :     , mHandle(aHandle)
    1080             :     , mHasFrecency(false)
    1081             :     , mHasExpirationTime(false)
    1082             :     , mHasHasAltData(false)
    1083             :     , mHasOnStartTime(false)
    1084          14 :     , mHasOnStopTime(false)
    1085             :   {
    1086          14 :     if (aFrecency) {
    1087          11 :       mHasFrecency = true;
    1088          11 :       mFrecency = *aFrecency;
    1089             :     }
    1090          14 :     if (aExpirationTime) {
    1091           7 :       mHasExpirationTime = true;
    1092           7 :       mExpirationTime = *aExpirationTime;
    1093             :     }
    1094          14 :     if (aHasAltData) {
    1095           5 :       mHasHasAltData = true;
    1096           5 :       mHasAltData = *aHasAltData;
    1097             :     }
    1098          14 :     if (aOnStartTime) {
    1099           6 :       mHasOnStartTime = true;
    1100           6 :       mOnStartTime = *aOnStartTime;
    1101             :     }
    1102          14 :     if (aOnStopTime) {
    1103           6 :       mHasOnStopTime = true;
    1104           6 :       mOnStopTime = *aOnStopTime;
    1105             :     }
    1106          14 :   }
    1107             : 
    1108             : protected:
    1109          28 :   ~UpdateIndexEntryEvent()
    1110          14 :   {
    1111          42 :   }
    1112             : 
    1113             : public:
    1114          14 :   NS_IMETHOD Run() override
    1115             :   {
    1116          14 :     if (mHandle->IsClosed() || mHandle->IsDoomed()) {
    1117           0 :       return NS_OK;
    1118             :     }
    1119             : 
    1120          70 :     CacheIndex::UpdateEntry(mHandle->Hash(),
    1121          14 :                             mHasFrecency ? &mFrecency : nullptr,
    1122          14 :                             mHasExpirationTime ? &mExpirationTime : nullptr,
    1123          14 :                             mHasHasAltData ? &mHasAltData : nullptr,
    1124          14 :                             mHasOnStartTime ? &mOnStartTime : nullptr,
    1125          14 :                             mHasOnStopTime ? &mOnStopTime : nullptr,
    1126          14 :                             nullptr);
    1127          14 :     return NS_OK;
    1128             :   }
    1129             : 
    1130             : protected:
    1131             :   RefPtr<CacheFileHandle>   mHandle;
    1132             : 
    1133             :   bool                      mHasFrecency;
    1134             :   bool                      mHasExpirationTime;
    1135             :   bool                      mHasHasAltData;
    1136             :   bool                      mHasOnStartTime;
    1137             :   bool                      mHasOnStopTime;
    1138             : 
    1139             :   uint32_t                  mFrecency;
    1140             :   uint32_t                  mExpirationTime;
    1141             :   bool                      mHasAltData;
    1142             :   uint16_t                  mOnStartTime;
    1143             :   uint16_t                  mOnStopTime;
    1144             : };
    1145             : 
    1146             : class MetadataWriteScheduleEvent : public Runnable
    1147             : {
    1148             : public:
    1149             :   enum EMode {
    1150             :     SCHEDULE,
    1151             :     UNSCHEDULE,
    1152             :     SHUTDOWN
    1153             :   } mMode;
    1154             : 
    1155             :   RefPtr<CacheFile> mFile;
    1156             :   RefPtr<CacheFileIOManager> mIOMan;
    1157             : 
    1158          36 :   MetadataWriteScheduleEvent(CacheFileIOManager* aManager,
    1159             :                              CacheFile* aFile,
    1160             :                              EMode aMode)
    1161          36 :     : Runnable("net::MetadataWriteScheduleEvent")
    1162             :     , mMode(aMode)
    1163             :     , mFile(aFile)
    1164          36 :     , mIOMan(aManager)
    1165          36 :   { }
    1166             : 
    1167         108 :   virtual ~MetadataWriteScheduleEvent() { }
    1168             : 
    1169          36 :   NS_IMETHOD Run() override
    1170             :   {
    1171          72 :     RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
    1172          36 :     if (!ioMan) {
    1173           0 :       NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
    1174           0 :       return NS_OK;
    1175             :     }
    1176             : 
    1177          36 :     switch (mMode)
    1178             :     {
    1179             :     case SCHEDULE:
    1180          32 :       ioMan->ScheduleMetadataWriteInternal(mFile);
    1181          32 :       break;
    1182             :     case UNSCHEDULE:
    1183           4 :       ioMan->UnscheduleMetadataWriteInternal(mFile);
    1184           4 :       break;
    1185             :     case SHUTDOWN:
    1186           0 :       ioMan->ShutdownMetadataWriteSchedulingInternal();
    1187           0 :       break;
    1188             :     }
    1189          36 :     return NS_OK;
    1190             :   }
    1191             : };
    1192             : 
    1193           3 : StaticRefPtr<CacheFileIOManager> CacheFileIOManager::gInstance;
    1194             : 
    1195         728 : NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
    1196             : 
    1197           1 : CacheFileIOManager::CacheFileIOManager()
    1198             :   : mShuttingDown(false)
    1199             :   , mTreeCreated(false)
    1200             :   , mTreeCreationFailed(false)
    1201             :   , mOverLimitEvicting(false)
    1202             :   , mCacheSizeOnHardLimit(false)
    1203           1 :   , mRemovingTrashDirs(false)
    1204             : {
    1205           1 :   LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
    1206           1 :   MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
    1207           1 : }
    1208             : 
    1209           0 : CacheFileIOManager::~CacheFileIOManager()
    1210             : {
    1211           0 :   LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
    1212           0 : }
    1213             : 
    1214             : // static
    1215             : nsresult
    1216           2 : CacheFileIOManager::Init()
    1217             : {
    1218           2 :   LOG(("CacheFileIOManager::Init()"));
    1219             : 
    1220           2 :   MOZ_ASSERT(NS_IsMainThread());
    1221             : 
    1222           2 :   if (gInstance) {
    1223           1 :     return NS_ERROR_ALREADY_INITIALIZED;
    1224             :   }
    1225             : 
    1226           2 :   RefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
    1227             : 
    1228           1 :   nsresult rv = ioMan->InitInternal();
    1229           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1230             : 
    1231           1 :   gInstance = ioMan.forget();
    1232           1 :   return NS_OK;
    1233             : }
    1234             : 
    1235             : nsresult
    1236           1 : CacheFileIOManager::InitInternal()
    1237             : {
    1238             :   nsresult rv;
    1239             : 
    1240           1 :   mIOThread = new CacheIOThread();
    1241             : 
    1242           1 :   rv = mIOThread->Init();
    1243           1 :   MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
    1244           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1245             : 
    1246           1 :   mStartTime = TimeStamp::NowLoRes();
    1247             : 
    1248           1 :   return NS_OK;
    1249             : }
    1250             : 
    1251             : // static
    1252             : nsresult
    1253           0 : CacheFileIOManager::Shutdown()
    1254             : {
    1255           0 :   LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance.get()));
    1256             : 
    1257           0 :   MOZ_ASSERT(NS_IsMainThread());
    1258             : 
    1259           0 :   if (!gInstance) {
    1260           0 :     return NS_ERROR_NOT_INITIALIZED;
    1261             :   }
    1262             : 
    1263           0 :   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
    1264             : 
    1265           0 :   CacheIndex::PreShutdown();
    1266             : 
    1267           0 :   ShutdownMetadataWriteScheduling();
    1268             : 
    1269           0 :   RefPtr<ShutdownEvent> ev = new ShutdownEvent();
    1270           0 :   ev->PostAndWait();
    1271             : 
    1272           0 :   MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
    1273           0 :   MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
    1274             : 
    1275           0 :   if (gInstance->mIOThread) {
    1276           0 :     gInstance->mIOThread->Shutdown();
    1277             :   }
    1278             : 
    1279           0 :   CacheIndex::Shutdown();
    1280             : 
    1281           0 :   if (CacheObserver::ClearCacheOnShutdown()) {
    1282           0 :     Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE2_SHUTDOWN_CLEAR_PRIVATE> totalTimer;
    1283           0 :     gInstance->SyncRemoveAllCacheFiles();
    1284             :   }
    1285             : 
    1286           0 :   gInstance = nullptr;
    1287             : 
    1288           0 :   return NS_OK;
    1289             : }
    1290             : 
    1291             : nsresult
    1292           0 : CacheFileIOManager::ShutdownInternal()
    1293             : {
    1294           0 :   LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
    1295             : 
    1296           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    1297             : 
    1298             :   // No new handles can be created after this flag is set
    1299           0 :   mShuttingDown = true;
    1300             : 
    1301           0 :   if (mTrashTimer) {
    1302           0 :     mTrashTimer->Cancel();
    1303           0 :     mTrashTimer = nullptr;
    1304             :   }
    1305             : 
    1306             :   // close all handles and delete all associated files
    1307           0 :   nsTArray<RefPtr<CacheFileHandle> > handles;
    1308           0 :   mHandles.GetAllHandles(&handles);
    1309           0 :   handles.AppendElements(mSpecialHandles);
    1310             : 
    1311           0 :   for (uint32_t i=0 ; i<handles.Length() ; i++) {
    1312           0 :     CacheFileHandle *h = handles[i];
    1313           0 :     h->mClosed = true;
    1314             : 
    1315           0 :     h->Log();
    1316             : 
    1317             :     // Close completely written files.
    1318           0 :     MaybeReleaseNSPRHandleInternal(h);
    1319             :     // Don't bother removing invalid and/or doomed files to improve
    1320             :     // shutdown perfomrance.
    1321             :     // Doomed files are already in the doomed directory from which
    1322             :     // we never reuse files and delete the dir on next session startup.
    1323             :     // Invalid files don't have metadata and thus won't load anyway
    1324             :     // (hashes won't match).
    1325             : 
    1326           0 :     if (!h->IsSpecialFile() && !h->mIsDoomed && !h->mFileExists) {
    1327           0 :       CacheIndex::RemoveEntry(h->Hash());
    1328             :     }
    1329             : 
    1330             :     // Remove the handle from mHandles/mSpecialHandles
    1331           0 :     if (h->IsSpecialFile()) {
    1332           0 :       mSpecialHandles.RemoveElement(h);
    1333             :     } else {
    1334           0 :       mHandles.RemoveHandle(h);
    1335             :     }
    1336             : 
    1337             :     // Pointer to the hash is no longer valid once the last handle with the
    1338             :     // given hash is released. Null out the pointer so that we crash if there
    1339             :     // is a bug in this code and we dereference the pointer after this point.
    1340           0 :     if (!h->IsSpecialFile()) {
    1341           0 :       h->mHash = nullptr;
    1342             :     }
    1343             :   }
    1344             : 
    1345             :   // Assert the table is empty. When we are here, no new handles can be added
    1346             :   // and handles will no longer remove them self from this table and we don't
    1347             :   // want to keep invalid handles here. Also, there is no lookup after this
    1348             :   // point to happen.
    1349           0 :   MOZ_ASSERT(mHandles.HandleCount() == 0);
    1350             : 
    1351             :   // Release trash directory enumerator
    1352           0 :   if (mTrashDirEnumerator) {
    1353           0 :     mTrashDirEnumerator->Close();
    1354           0 :     mTrashDirEnumerator = nullptr;
    1355             :   }
    1356             : 
    1357           0 :   return NS_OK;
    1358             : }
    1359             : 
    1360             : // static
    1361             : nsresult
    1362           1 : CacheFileIOManager::OnProfile()
    1363             : {
    1364           1 :   LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance.get()));
    1365             : 
    1366           2 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1367           1 :   if (!ioMan) {
    1368             :     // CacheFileIOManager::Init() failed, probably could not create the IO
    1369             :     // thread, just go with it...
    1370           0 :     return NS_ERROR_NOT_INITIALIZED;
    1371             :   }
    1372             : 
    1373             :   nsresult rv;
    1374             : 
    1375           2 :   nsCOMPtr<nsIFile> directory;
    1376             : 
    1377           1 :   CacheObserver::ParentDirOverride(getter_AddRefs(directory));
    1378             : 
    1379             : #if defined(MOZ_WIDGET_ANDROID)
    1380             :   nsCOMPtr<nsIFile> profilelessDirectory;
    1381             :   char* cachePath = getenv("CACHE_DIRECTORY");
    1382             :   if (!directory && cachePath && *cachePath) {
    1383             :     rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
    1384             :                                true, getter_AddRefs(directory));
    1385             :     if (NS_SUCCEEDED(rv)) {
    1386             :       // Save this directory as the profileless path.
    1387             :       rv = directory->Clone(getter_AddRefs(profilelessDirectory));
    1388             :       NS_ENSURE_SUCCESS(rv, rv);
    1389             : 
    1390             :       // Add profile leaf name to the directory name to distinguish
    1391             :       // multiple profiles Fennec supports.
    1392             :       nsCOMPtr<nsIFile> profD;
    1393             :       rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    1394             :                                   getter_AddRefs(profD));
    1395             : 
    1396             :       nsAutoCString leafName;
    1397             :       if (NS_SUCCEEDED(rv)) {
    1398             :         rv = profD->GetNativeLeafName(leafName);
    1399             :       }
    1400             :       if (NS_SUCCEEDED(rv)) {
    1401             :         rv = directory->AppendNative(leafName);
    1402             :       }
    1403             :       if (NS_FAILED(rv)) {
    1404             :         directory = nullptr;
    1405             :       }
    1406             :     }
    1407             :   }
    1408             : #endif
    1409             : 
    1410           1 :   if (!directory) {
    1411           1 :     rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
    1412           2 :                                 getter_AddRefs(directory));
    1413             :   }
    1414             : 
    1415           1 :   if (!directory) {
    1416           1 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
    1417           2 :                                 getter_AddRefs(directory));
    1418             :   }
    1419             : 
    1420           1 :   if (directory) {
    1421           1 :     rv = directory->Append(NS_LITERAL_STRING("cache2"));
    1422           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1423             :   }
    1424             : 
    1425             :   // All functions return a clone.
    1426           1 :   ioMan->mCacheDirectory.swap(directory);
    1427             : 
    1428             : #if defined(MOZ_WIDGET_ANDROID)
    1429             :   if (profilelessDirectory) {
    1430             :     rv = profilelessDirectory->Append(NS_LITERAL_STRING("cache2"));
    1431             :     NS_ENSURE_SUCCESS(rv, rv);
    1432             :   }
    1433             : 
    1434             :   ioMan->mCacheProfilelessDirectory.swap(profilelessDirectory);
    1435             : #endif
    1436             : 
    1437           1 :   if (ioMan->mCacheDirectory) {
    1438           1 :     CacheIndex::Init(ioMan->mCacheDirectory);
    1439             :   }
    1440             : 
    1441           1 :   return NS_OK;
    1442             : }
    1443             : 
    1444             : // static
    1445             : already_AddRefed<nsIEventTarget>
    1446         114 : CacheFileIOManager::IOTarget()
    1447             : {
    1448         228 :   nsCOMPtr<nsIEventTarget> target;
    1449         114 :   if (gInstance && gInstance->mIOThread) {
    1450         114 :     target = gInstance->mIOThread->Target();
    1451             :   }
    1452             : 
    1453         228 :   return target.forget();
    1454             : }
    1455             : 
    1456             : // static
    1457             : already_AddRefed<CacheIOThread>
    1458          18 : CacheFileIOManager::IOThread()
    1459             : {
    1460          36 :   RefPtr<CacheIOThread> thread;
    1461          18 :   if (gInstance) {
    1462          18 :     thread = gInstance->mIOThread;
    1463             :   }
    1464             : 
    1465          36 :   return thread.forget();
    1466             : }
    1467             : 
    1468             : // static
    1469             : bool
    1470          44 : CacheFileIOManager::IsOnIOThread()
    1471             : {
    1472          88 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1473          44 :   if (ioMan && ioMan->mIOThread) {
    1474          44 :     return ioMan->mIOThread->IsCurrentThread();
    1475             :   }
    1476             : 
    1477           0 :   return false;
    1478             : }
    1479             : 
    1480             : // static
    1481             : bool
    1482         153 : CacheFileIOManager::IsOnIOThreadOrCeased()
    1483             : {
    1484         306 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1485         153 :   if (ioMan && ioMan->mIOThread) {
    1486         153 :     return ioMan->mIOThread->IsCurrentThread();
    1487             :   }
    1488             : 
    1489             :   // Ceased...
    1490           0 :   return true;
    1491             : }
    1492             : 
    1493             : // static
    1494             : bool
    1495           0 : CacheFileIOManager::IsShutdown()
    1496             : {
    1497           0 :   if (!gInstance) {
    1498           0 :     return true;
    1499             :   }
    1500           0 :   return gInstance->mShuttingDown;
    1501             : }
    1502             : 
    1503             : // static
    1504             : nsresult
    1505          32 : CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
    1506             : {
    1507          64 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1508          32 :   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
    1509             : 
    1510          32 :   NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
    1511             : 
    1512             :   RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
    1513          96 :     ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
    1514          64 :   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
    1515          32 :   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
    1516          32 :   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    1517             : }
    1518             : 
    1519             : nsresult
    1520          32 : CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
    1521             : {
    1522          32 :   MOZ_ASSERT(IsOnIOThreadOrCeased());
    1523             : 
    1524             :   nsresult rv;
    1525             : 
    1526          32 :   if (!mMetadataWritesTimer) {
    1527           1 :     mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    1528           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1529             : 
    1530           2 :     rv = mMetadataWritesTimer->InitWithCallback(
    1531           1 :       this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
    1532           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1533             :   }
    1534             : 
    1535          32 :   if (mScheduledMetadataWrites.IndexOf(aFile) !=
    1536             :       mScheduledMetadataWrites.NoIndex) {
    1537          26 :     return NS_OK;
    1538             :   }
    1539             : 
    1540           6 :   mScheduledMetadataWrites.AppendElement(aFile);
    1541             : 
    1542           6 :   return NS_OK;
    1543             : }
    1544             : 
    1545             : // static
    1546             : nsresult
    1547           4 : CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
    1548             : {
    1549           8 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1550           4 :   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
    1551             : 
    1552           4 :   NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
    1553             : 
    1554             :   RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
    1555          12 :     ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
    1556           8 :   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
    1557           4 :   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
    1558           4 :   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    1559             : }
    1560             : 
    1561             : nsresult
    1562           4 : CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
    1563             : {
    1564           4 :   MOZ_ASSERT(IsOnIOThreadOrCeased());
    1565             : 
    1566           4 :   mScheduledMetadataWrites.RemoveElement(aFile);
    1567             : 
    1568           4 :   if (mScheduledMetadataWrites.Length() == 0 &&
    1569           0 :       mMetadataWritesTimer) {
    1570           0 :     mMetadataWritesTimer->Cancel();
    1571           0 :     mMetadataWritesTimer = nullptr;
    1572             :   }
    1573             : 
    1574           4 :   return NS_OK;
    1575             : }
    1576             : 
    1577             : // static
    1578             : nsresult
    1579           0 : CacheFileIOManager::ShutdownMetadataWriteScheduling()
    1580             : {
    1581           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1582           0 :   NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
    1583             : 
    1584             :   RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
    1585           0 :     ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
    1586           0 :   nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
    1587           0 :   NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
    1588           0 :   return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    1589             : }
    1590             : 
    1591             : nsresult
    1592           0 : CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
    1593             : {
    1594           0 :   MOZ_ASSERT(IsOnIOThreadOrCeased());
    1595             : 
    1596           0 :   nsTArray<RefPtr<CacheFile> > files;
    1597           0 :   files.SwapElements(mScheduledMetadataWrites);
    1598           0 :   for (uint32_t i = 0; i < files.Length(); ++i) {
    1599           0 :     CacheFile * file = files[i];
    1600           0 :     file->WriteMetadataIfNeeded();
    1601             :   }
    1602             : 
    1603           0 :   if (mMetadataWritesTimer) {
    1604           0 :     mMetadataWritesTimer->Cancel();
    1605           0 :     mMetadataWritesTimer = nullptr;
    1606             :   }
    1607             : 
    1608           0 :   return NS_OK;
    1609             : }
    1610             : 
    1611             : NS_IMETHODIMP
    1612           0 : CacheFileIOManager::Notify(nsITimer * aTimer)
    1613             : {
    1614           0 :   MOZ_ASSERT(IsOnIOThreadOrCeased());
    1615           0 :   MOZ_ASSERT(mMetadataWritesTimer == aTimer);
    1616             : 
    1617           0 :   mMetadataWritesTimer = nullptr;
    1618             : 
    1619           0 :   nsTArray<RefPtr<CacheFile> > files;
    1620           0 :   files.SwapElements(mScheduledMetadataWrites);
    1621           0 :   for (uint32_t i = 0; i < files.Length(); ++i) {
    1622           0 :     CacheFile * file = files[i];
    1623           0 :     file->WriteMetadataIfNeeded();
    1624             :   }
    1625             : 
    1626           0 :   return NS_OK;
    1627             : }
    1628             : 
    1629             : // static
    1630             : nsresult
    1631           8 : CacheFileIOManager::OpenFile(const nsACString &aKey,
    1632             :                              uint32_t aFlags, CacheFileIOListener *aCallback)
    1633             : {
    1634           8 :   LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
    1635             :        PromiseFlatCString(aKey).get(), aFlags, aCallback));
    1636             : 
    1637             :   nsresult rv;
    1638          16 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1639             : 
    1640           8 :   if (!ioMan) {
    1641           0 :     return NS_ERROR_NOT_INITIALIZED;
    1642             :   }
    1643             : 
    1644           8 :   bool priority = aFlags & CacheFileIOManager::PRIORITY;
    1645          16 :   RefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aCallback);
    1646          16 :   rv = ioMan->mIOThread->Dispatch(ev, priority
    1647             :     ? CacheIOThread::OPEN_PRIORITY
    1648          16 :     : CacheIOThread::OPEN);
    1649           8 :   NS_ENSURE_SUCCESS(rv, rv);
    1650             : 
    1651           8 :   return NS_OK;
    1652             : }
    1653             : 
    1654             : nsresult
    1655           5 : CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
    1656             :                                      const nsACString &aKey,
    1657             :                                      uint32_t aFlags,
    1658             :                                      CacheFileHandle **_retval)
    1659             : {
    1660           5 :   LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
    1661             :        "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
    1662             :        aFlags));
    1663             : 
    1664           5 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    1665             : 
    1666             :   nsresult rv;
    1667             : 
    1668           5 :   if (mShuttingDown) {
    1669           0 :     return NS_ERROR_NOT_INITIALIZED;
    1670             :   }
    1671             : 
    1672          10 :   CacheIOThread::Cancelable cancelable(true /* never called for special handles */);
    1673             : 
    1674           5 :   if (!mTreeCreated) {
    1675           0 :     rv = CreateCacheTree();
    1676           0 :     if (NS_FAILED(rv)) return rv;
    1677             :   }
    1678             : 
    1679           5 :   CacheFileHandle::PinningStatus pinning = aFlags & PINNED
    1680           5 :     ? CacheFileHandle::PinningStatus::PINNED
    1681           5 :     : CacheFileHandle::PinningStatus::NON_PINNED;
    1682             : 
    1683          10 :   nsCOMPtr<nsIFile> file;
    1684           5 :   rv = GetFile(aHash, getter_AddRefs(file));
    1685           5 :   NS_ENSURE_SUCCESS(rv, rv);
    1686             : 
    1687          10 :   RefPtr<CacheFileHandle> handle;
    1688           5 :   mHandles.GetHandle(aHash, getter_AddRefs(handle));
    1689             : 
    1690           5 :   if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
    1691           0 :     if (handle) {
    1692           0 :       rv = DoomFileInternal(handle);
    1693           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1694           0 :       handle = nullptr;
    1695             :     }
    1696             : 
    1697           0 :     rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
    1698           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1699             : 
    1700             :     bool exists;
    1701           0 :     rv = file->Exists(&exists);
    1702           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1703             : 
    1704           0 :     if (exists) {
    1705           0 :       CacheIndex::RemoveEntry(aHash);
    1706             : 
    1707           0 :       LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
    1708             :            "disk"));
    1709           0 :       rv = file->Remove(false);
    1710           0 :       if (NS_FAILED(rv)) {
    1711           0 :         NS_WARNING("Cannot remove old entry from the disk");
    1712           0 :         LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
    1713             :              ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    1714             :       }
    1715             :     }
    1716             : 
    1717           0 :     CacheIndex::AddEntry(aHash);
    1718           0 :     handle->mFile.swap(file);
    1719           0 :     handle->mFileSize = 0;
    1720             :   }
    1721             : 
    1722           5 :   if (handle) {
    1723           0 :     handle.swap(*_retval);
    1724           0 :     return NS_OK;
    1725             :   }
    1726             : 
    1727           5 :   bool exists, evictedAsPinned = false, evictedAsNonPinned = false;
    1728           5 :   rv = file->Exists(&exists);
    1729           5 :   NS_ENSURE_SUCCESS(rv, rv);
    1730             : 
    1731           5 :   if (exists && mContextEvictor) {
    1732           0 :     if (mContextEvictor->ContextsCount() == 0) {
    1733           0 :       mContextEvictor = nullptr;
    1734             :     } else {
    1735           0 :       mContextEvictor->WasEvicted(aKey, file, &evictedAsPinned, &evictedAsNonPinned);
    1736             :     }
    1737             :   }
    1738             : 
    1739           5 :   if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
    1740           0 :     return NS_ERROR_NOT_AVAILABLE;
    1741             :   }
    1742             : 
    1743           5 :   if (exists) {
    1744             :     // For existing files we determine the pinning status later, after the metadata gets parsed.
    1745           3 :     pinning = CacheFileHandle::PinningStatus::UNKNOWN;
    1746             :   }
    1747             : 
    1748           5 :   rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
    1749           5 :   NS_ENSURE_SUCCESS(rv, rv);
    1750             : 
    1751           5 :   if (exists) {
    1752             :     // If this file has been found evicted through the context file evictor above for
    1753             :     // any of pinned or non-pinned state, these calls ensure we doom the handle ASAP
    1754             :     // we know the real pinning state after metadta has been parsed.  DoomFileInternal
    1755             :     // on the |handle| doesn't doom right now, since the pinning state is unknown
    1756             :     // and we pass down a pinning restriction.
    1757           3 :     if (evictedAsPinned) {
    1758           0 :       rv = DoomFileInternal(handle, DOOM_WHEN_PINNED);
    1759           0 :       MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
    1760             :     }
    1761           3 :     if (evictedAsNonPinned) {
    1762           0 :       rv = DoomFileInternal(handle, DOOM_WHEN_NON_PINNED);
    1763           0 :       MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
    1764             :     }
    1765             : 
    1766           3 :     rv = file->GetFileSize(&handle->mFileSize);
    1767           3 :     NS_ENSURE_SUCCESS(rv, rv);
    1768             : 
    1769           3 :     handle->mFileExists = true;
    1770             : 
    1771           3 :     CacheIndex::EnsureEntryExists(aHash);
    1772             :   } else {
    1773           2 :     handle->mFileSize = 0;
    1774             : 
    1775           2 :     CacheIndex::AddEntry(aHash);
    1776             :   }
    1777             : 
    1778           5 :   handle->mFile.swap(file);
    1779           5 :   handle.swap(*_retval);
    1780           5 :   return NS_OK;
    1781             : }
    1782             : 
    1783             : nsresult
    1784           3 : CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
    1785             :                                             uint32_t aFlags,
    1786             :                                             CacheFileHandle **_retval)
    1787             : {
    1788           3 :   LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
    1789             :        PromiseFlatCString(aKey).get(), aFlags));
    1790             : 
    1791           3 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    1792             : 
    1793             :   nsresult rv;
    1794             : 
    1795           3 :   if (mShuttingDown) {
    1796           0 :     return NS_ERROR_NOT_INITIALIZED;
    1797             :   }
    1798             : 
    1799           3 :   if (!mTreeCreated) {
    1800           1 :     rv = CreateCacheTree();
    1801           1 :     if (NS_FAILED(rv)) return rv;
    1802             :   }
    1803             : 
    1804           6 :   nsCOMPtr<nsIFile> file;
    1805           3 :   rv = GetSpecialFile(aKey, getter_AddRefs(file));
    1806           3 :   NS_ENSURE_SUCCESS(rv, rv);
    1807             : 
    1808           6 :   RefPtr<CacheFileHandle> handle;
    1809           3 :   for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
    1810           0 :     if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
    1811           0 :       handle = mSpecialHandles[i];
    1812           0 :       break;
    1813             :     }
    1814             :   }
    1815             : 
    1816           3 :   if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
    1817           0 :     if (handle) {
    1818           0 :       rv = DoomFileInternal(handle);
    1819           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1820           0 :       handle = nullptr;
    1821             :     }
    1822             : 
    1823           0 :     handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
    1824           0 :     mSpecialHandles.AppendElement(handle);
    1825             : 
    1826             :     bool exists;
    1827           0 :     rv = file->Exists(&exists);
    1828           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1829             : 
    1830           0 :     if (exists) {
    1831           0 :       LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
    1832             :            "disk"));
    1833           0 :       rv = file->Remove(false);
    1834           0 :       if (NS_FAILED(rv)) {
    1835           0 :         NS_WARNING("Cannot remove old entry from the disk");
    1836           0 :         LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
    1837             :              "failed. [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    1838             :       }
    1839             :     }
    1840             : 
    1841           0 :     handle->mFile.swap(file);
    1842           0 :     handle->mFileSize = 0;
    1843             :   }
    1844             : 
    1845           3 :   if (handle) {
    1846           0 :     handle.swap(*_retval);
    1847           0 :     return NS_OK;
    1848             :   }
    1849             : 
    1850             :   bool exists;
    1851           3 :   rv = file->Exists(&exists);
    1852           3 :   NS_ENSURE_SUCCESS(rv, rv);
    1853             : 
    1854           3 :   if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
    1855           3 :     return NS_ERROR_NOT_AVAILABLE;
    1856             :   }
    1857             : 
    1858           0 :   handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
    1859           0 :   mSpecialHandles.AppendElement(handle);
    1860             : 
    1861           0 :   if (exists) {
    1862           0 :     rv = file->GetFileSize(&handle->mFileSize);
    1863           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1864             : 
    1865           0 :     handle->mFileExists = true;
    1866             :   } else {
    1867           0 :     handle->mFileSize = 0;
    1868             :   }
    1869             : 
    1870           0 :   handle->mFile.swap(file);
    1871           0 :   handle.swap(*_retval);
    1872           0 :   return NS_OK;
    1873             : }
    1874             : 
    1875             : nsresult
    1876           0 : CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
    1877             : {
    1878             :   nsresult rv;
    1879             : 
    1880           0 :   LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
    1881             : 
    1882           0 :   MOZ_ASSERT(!aHandle->IsClosed());
    1883             : 
    1884           0 :   aHandle->Log();
    1885             : 
    1886           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    1887             : 
    1888           0 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    1889             : 
    1890             :   // Maybe close file handle (can be legally bypassed after shutdown)
    1891           0 :   rv = MaybeReleaseNSPRHandleInternal(aHandle);
    1892             : 
    1893             :   // Delete the file if the entry was doomed or invalid and
    1894             :   // filedesc properly closed
    1895           0 :   if ((aHandle->mIsDoomed || aHandle->mInvalid) && aHandle->mFileExists &&
    1896           0 :        NS_SUCCEEDED(rv)) {
    1897           0 :     LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
    1898             :          "disk"));
    1899             : 
    1900           0 :     rv = aHandle->mFile->Remove(false);
    1901           0 :     if (NS_SUCCEEDED(rv)) {
    1902           0 :       aHandle->mFileExists = false;
    1903             :     } else {
    1904           0 :       LOG(("  failed to remove the file [rv=0x%08" PRIx32 "]",
    1905             :            static_cast<uint32_t>(rv)));
    1906             :     }
    1907             :   }
    1908             : 
    1909           0 :   if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
    1910           0 :       (aHandle->mInvalid || !aHandle->mFileExists)) {
    1911           0 :     CacheIndex::RemoveEntry(aHandle->Hash());
    1912             :   }
    1913             : 
    1914             :   // Don't remove handles after shutdown
    1915           0 :   if (!mShuttingDown) {
    1916           0 :     if (aHandle->IsSpecialFile()) {
    1917           0 :       mSpecialHandles.RemoveElement(aHandle);
    1918             :     } else {
    1919           0 :       mHandles.RemoveHandle(aHandle);
    1920             :     }
    1921             :   }
    1922             : 
    1923           0 :   return NS_OK;
    1924             : }
    1925             : 
    1926             : // static
    1927             : nsresult
    1928           5 : CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
    1929             :                          char *aBuf, int32_t aCount,
    1930             :                          CacheFileIOListener *aCallback)
    1931             : {
    1932           5 :   LOG(("CacheFileIOManager::Read() [handle=%p, offset=%" PRId64 ", count=%d, "
    1933             :        "listener=%p]", aHandle, aOffset, aCount, aCallback));
    1934             : 
    1935           5 :   if (CacheObserver::ShuttingDown()) {
    1936           0 :     LOG(("  no reads after shutdown"));
    1937           0 :     return NS_ERROR_NOT_INITIALIZED;
    1938             :   }
    1939             : 
    1940             :   nsresult rv;
    1941          10 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    1942             : 
    1943           5 :   if (aHandle->IsClosed() || !ioMan) {
    1944           0 :     return NS_ERROR_NOT_INITIALIZED;
    1945             :   }
    1946             : 
    1947             :   RefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
    1948          10 :                                          aCallback);
    1949          10 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
    1950             :     ? CacheIOThread::READ_PRIORITY
    1951          10 :     : CacheIOThread::READ);
    1952           5 :   NS_ENSURE_SUCCESS(rv, rv);
    1953             : 
    1954           5 :   return NS_OK;
    1955             : }
    1956             : 
    1957             : nsresult
    1958           5 : CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
    1959             :                                  char *aBuf, int32_t aCount)
    1960             : {
    1961           5 :   LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%" PRId64 ", count=%d]",
    1962             :        aHandle, aOffset, aCount));
    1963             : 
    1964             :   nsresult rv;
    1965             : 
    1966           5 :   if (CacheObserver::ShuttingDown()) {
    1967           0 :     LOG(("  no reads after shutdown"));
    1968           0 :     return NS_ERROR_NOT_INITIALIZED;
    1969             :   }
    1970             : 
    1971           5 :   if (!aHandle->mFileExists) {
    1972           0 :     NS_WARNING("Trying to read from non-existent file");
    1973           0 :     return NS_ERROR_NOT_AVAILABLE;
    1974             :   }
    1975             : 
    1976          10 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    1977             : 
    1978           5 :   if (!aHandle->mFD) {
    1979           3 :     rv = OpenNSPRHandle(aHandle);
    1980           3 :     NS_ENSURE_SUCCESS(rv, rv);
    1981             :   } else {
    1982           2 :     NSPRHandleUsed(aHandle);
    1983             :   }
    1984             : 
    1985             :   // Check again, OpenNSPRHandle could figure out the file was gone.
    1986           5 :   if (!aHandle->mFileExists) {
    1987           0 :     NS_WARNING("Trying to read from non-existent file");
    1988           0 :     return NS_ERROR_NOT_AVAILABLE;
    1989             :   }
    1990             : 
    1991           5 :   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
    1992           5 :   if (offset == -1) {
    1993           0 :     return NS_ERROR_FAILURE;
    1994             :   }
    1995             : 
    1996           5 :   int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
    1997           5 :   if (bytesRead != aCount) {
    1998           0 :     return NS_ERROR_FAILURE;
    1999             :   }
    2000             : 
    2001           5 :   return NS_OK;
    2002             : }
    2003             : 
    2004             : // static
    2005             : nsresult
    2006           6 : CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
    2007             :                           const char *aBuf, int32_t aCount, bool aValidate,
    2008             :                           bool aTruncate, CacheFileIOListener *aCallback)
    2009             : {
    2010           6 :   LOG(("CacheFileIOManager::Write() [handle=%p, offset=%" PRId64 ", count=%d, "
    2011             :        "validate=%d, truncate=%d, listener=%p]", aHandle, aOffset, aCount,
    2012             :        aValidate, aTruncate, aCallback));
    2013             : 
    2014             :   nsresult rv;
    2015          12 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2016             : 
    2017           6 :   if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) {
    2018           0 :     if (!aCallback) {
    2019             :       // When no callback is provided, CacheFileIOManager is responsible for
    2020             :       // releasing the buffer. We must release it even in case of failure.
    2021           0 :       free(const_cast<char *>(aBuf));
    2022             :     }
    2023           0 :     return NS_ERROR_NOT_INITIALIZED;
    2024             :   }
    2025             : 
    2026             :   RefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
    2027          12 :                                            aValidate, aTruncate, aCallback);
    2028          12 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    2029             :                                   ? CacheIOThread::WRITE_PRIORITY
    2030          12 :                                   : CacheIOThread::WRITE);
    2031           6 :   NS_ENSURE_SUCCESS(rv, rv);
    2032             : 
    2033           6 :   return NS_OK;
    2034             : }
    2035             : 
    2036             : static nsresult
    2037           4 : TruncFile(PRFileDesc *aFD, int64_t aEOF)
    2038             : {
    2039             : #if defined(XP_UNIX)
    2040           4 :   if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
    2041           0 :     NS_ERROR("ftruncate failed");
    2042           0 :     return NS_ERROR_FAILURE;
    2043             :   }
    2044             : #elif defined(XP_WIN)
    2045             :   int64_t cnt = PR_Seek64(aFD, aEOF, PR_SEEK_SET);
    2046             :   if (cnt == -1) {
    2047             :     return NS_ERROR_FAILURE;
    2048             :   }
    2049             :   if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
    2050             :     NS_ERROR("SetEndOfFile failed");
    2051             :     return NS_ERROR_FAILURE;
    2052             :   }
    2053             : #else
    2054             :   MOZ_ASSERT(false, "Not implemented!");
    2055             :   return NS_ERROR_NOT_IMPLEMENTED;
    2056             : #endif
    2057             : 
    2058           4 :   return NS_OK;
    2059             : }
    2060             : 
    2061             : nsresult
    2062           6 : CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
    2063             :                                   const char *aBuf, int32_t aCount,
    2064             :                                   bool aValidate, bool aTruncate)
    2065             : {
    2066           6 :   LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%" PRId64 ", count=%d, "
    2067             :        "validate=%d, truncate=%d]", aHandle, aOffset, aCount, aValidate,
    2068             :        aTruncate));
    2069             : 
    2070             :   nsresult rv;
    2071             : 
    2072           6 :   if (aHandle->mKilled) {
    2073           0 :     LOG(("  handle already killed, nothing written"));
    2074           0 :     return NS_OK;
    2075             :   }
    2076             : 
    2077           6 :   if (CacheObserver::ShuttingDown() && (!aValidate || !aHandle->mFD)) {
    2078           0 :     aHandle->mKilled = true;
    2079           0 :     LOG(("  killing the handle, nothing written"));
    2080           0 :     return NS_OK;
    2081             :   }
    2082             : 
    2083           6 :   if (CacheObserver::IsPastShutdownIOLag()) {
    2084           0 :     LOG(("  past the shutdown I/O lag, nothing written"));
    2085             :     // Pretend the write has succeeded, otherwise upper layers will doom
    2086             :     // the file and we end up with I/O anyway.
    2087           0 :     return NS_OK;
    2088             :   }
    2089             : 
    2090          12 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    2091             : 
    2092           6 :   if (!aHandle->mFileExists) {
    2093           2 :     rv = CreateFile(aHandle);
    2094           2 :     NS_ENSURE_SUCCESS(rv, rv);
    2095             :   }
    2096             : 
    2097           6 :   if (!aHandle->mFD) {
    2098           0 :     rv = OpenNSPRHandle(aHandle);
    2099           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2100             :   } else {
    2101           6 :     NSPRHandleUsed(aHandle);
    2102             :   }
    2103             : 
    2104             :   // Check again, OpenNSPRHandle could figure out the file was gone.
    2105           6 :   if (!aHandle->mFileExists) {
    2106           0 :     return NS_ERROR_NOT_AVAILABLE;
    2107             :   }
    2108             : 
    2109             :   // When this operation would increase cache size, check whether the cache size
    2110             :   // reached the hard limit and whether it would cause critical low disk space.
    2111           6 :   if (aHandle->mFileSize < aOffset + aCount) {
    2112           4 :     if (mOverLimitEvicting && mCacheSizeOnHardLimit) {
    2113           0 :       LOG(("CacheFileIOManager::WriteInternal() - failing because cache size "
    2114             :            "reached hard limit!"));
    2115           0 :       return NS_ERROR_FILE_DISK_FULL;
    2116             :     }
    2117             : 
    2118           4 :     int64_t freeSpace = -1;
    2119           4 :     rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
    2120           4 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2121           0 :       LOG(("CacheFileIOManager::WriteInternal() - GetDiskSpaceAvailable() "
    2122             :            "failed! [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    2123             :     } else {
    2124           4 :       uint32_t limit = CacheObserver::DiskFreeSpaceHardLimit();
    2125           4 :       if (freeSpace - aOffset - aCount + aHandle->mFileSize < limit) {
    2126           0 :         LOG(("CacheFileIOManager::WriteInternal() - Low free space, refusing "
    2127             :              "to write! [freeSpace=%" PRId64 ", limit=%u]", freeSpace, limit));
    2128           0 :         return NS_ERROR_FILE_DISK_FULL;
    2129             :       }
    2130             :     }
    2131             :   }
    2132             : 
    2133             :   // Write invalidates the entry by default
    2134           6 :   aHandle->mInvalid = true;
    2135             : 
    2136           6 :   int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
    2137           6 :   if (offset == -1) {
    2138           0 :     return NS_ERROR_FAILURE;
    2139             :   }
    2140             : 
    2141           6 :   int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
    2142             : 
    2143           6 :   if (bytesWritten != -1) {
    2144           6 :     uint32_t oldSizeInK = aHandle->FileSizeInK();
    2145           6 :     int64_t writeEnd = aOffset + bytesWritten;
    2146             : 
    2147           6 :     if (aTruncate) {
    2148           4 :       rv = TruncFile(aHandle->mFD, writeEnd);
    2149           4 :       NS_ENSURE_SUCCESS(rv, rv);
    2150             : 
    2151           4 :       aHandle->mFileSize = writeEnd;
    2152             :     } else {
    2153           2 :       if (aHandle->mFileSize < writeEnd) {
    2154           2 :         aHandle->mFileSize = writeEnd;
    2155             :       }
    2156             :     }
    2157             : 
    2158           6 :     uint32_t newSizeInK = aHandle->FileSizeInK();
    2159             : 
    2160           9 :     if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
    2161           3 :         !aHandle->IsSpecialFile()) {
    2162           3 :       CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr,
    2163           3 :                               nullptr, nullptr, &newSizeInK);
    2164             : 
    2165           3 :       if (oldSizeInK < newSizeInK) {
    2166           3 :         EvictIfOverLimitInternal();
    2167             :       }
    2168             :     }
    2169             :   }
    2170             : 
    2171           6 :   if (bytesWritten != aCount) {
    2172           0 :     return NS_ERROR_FAILURE;
    2173             :   }
    2174             : 
    2175             :   // Write was successful and this write validates the entry (i.e. metadata)
    2176           6 :   if (aValidate) {
    2177           4 :     aHandle->mInvalid = false;
    2178             :   }
    2179             : 
    2180           6 :   return NS_OK;
    2181             : }
    2182             : 
    2183             : // static
    2184             : nsresult
    2185           0 : CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
    2186             :                              CacheFileIOListener *aCallback)
    2187             : {
    2188           0 :   LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
    2189             :        aHandle, aCallback));
    2190             : 
    2191             :   nsresult rv;
    2192           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2193             : 
    2194           0 :   if (aHandle->IsClosed() || !ioMan) {
    2195           0 :     return NS_ERROR_NOT_INITIALIZED;
    2196             :   }
    2197             : 
    2198           0 :   RefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
    2199           0 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
    2200             :     ? CacheIOThread::OPEN_PRIORITY
    2201           0 :     : CacheIOThread::OPEN);
    2202           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2203             : 
    2204           0 :   return NS_OK;
    2205             : }
    2206             : 
    2207             : nsresult
    2208           0 : CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle,
    2209             :                                      PinningDoomRestriction aPinningDoomRestriction)
    2210             : {
    2211           0 :   LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
    2212           0 :   aHandle->Log();
    2213             : 
    2214           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    2215             : 
    2216             :   nsresult rv;
    2217             : 
    2218           0 :   if (aHandle->IsDoomed()) {
    2219           0 :     return NS_OK;
    2220             :   }
    2221             : 
    2222           0 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    2223             : 
    2224           0 :   if (aPinningDoomRestriction > NO_RESTRICTION) {
    2225           0 :     switch (aHandle->mPinning) {
    2226             :     case CacheFileHandle::PinningStatus::NON_PINNED:
    2227           0 :       if (MOZ_LIKELY(aPinningDoomRestriction != DOOM_WHEN_NON_PINNED)) {
    2228           0 :         LOG(("  not dooming, it's a non-pinned handle"));
    2229           0 :         return NS_OK;
    2230             :       }
    2231             :       // Doom now
    2232           0 :       break;
    2233             : 
    2234             :     case CacheFileHandle::PinningStatus::PINNED:
    2235           0 :       if (MOZ_UNLIKELY(aPinningDoomRestriction != DOOM_WHEN_PINNED)) {
    2236           0 :         LOG(("  not dooming, it's a pinned handle"));
    2237           0 :         return NS_OK;
    2238             :       }
    2239             :       // Doom now
    2240           0 :       break;
    2241             : 
    2242             :     case CacheFileHandle::PinningStatus::UNKNOWN:
    2243           0 :       if (MOZ_LIKELY(aPinningDoomRestriction == DOOM_WHEN_NON_PINNED)) {
    2244           0 :         LOG(("  doom when non-pinned set"));
    2245           0 :         aHandle->mDoomWhenFoundNonPinned = true;
    2246           0 :       } else if (MOZ_UNLIKELY(aPinningDoomRestriction == DOOM_WHEN_PINNED)) {
    2247           0 :         LOG(("  doom when pinned set"));
    2248           0 :         aHandle->mDoomWhenFoundPinned = true;
    2249             :       }
    2250             : 
    2251           0 :       LOG(("  pinning status not known, deferring doom decision"));
    2252           0 :       return NS_OK;
    2253             :     }
    2254             :   }
    2255             : 
    2256           0 :   if (aHandle->mFileExists) {
    2257             :     // we need to move the current file to the doomed directory
    2258           0 :     rv = MaybeReleaseNSPRHandleInternal(aHandle, true);
    2259           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2260             : 
    2261             :     // find unused filename
    2262           0 :     nsCOMPtr<nsIFile> file;
    2263           0 :     rv = GetDoomedFile(getter_AddRefs(file));
    2264           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2265             : 
    2266           0 :     nsCOMPtr<nsIFile> parentDir;
    2267           0 :     rv = file->GetParent(getter_AddRefs(parentDir));
    2268           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2269             : 
    2270           0 :     nsAutoCString leafName;
    2271           0 :     rv = file->GetNativeLeafName(leafName);
    2272           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2273             : 
    2274           0 :     rv = aHandle->mFile->MoveToNative(parentDir, leafName);
    2275           0 :     if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
    2276           0 :       LOG(("  file already removed under our hands"));
    2277           0 :       aHandle->mFileExists = false;
    2278           0 :       rv = NS_OK;
    2279             :     } else {
    2280           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2281           0 :       aHandle->mFile.swap(file);
    2282             :     }
    2283             :   }
    2284             : 
    2285           0 :   if (!aHandle->IsSpecialFile()) {
    2286           0 :     CacheIndex::RemoveEntry(aHandle->Hash());
    2287             :   }
    2288             : 
    2289           0 :   aHandle->mIsDoomed = true;
    2290             : 
    2291           0 :   if (!aHandle->IsSpecialFile()) {
    2292           0 :     RefPtr<CacheStorageService> storageService = CacheStorageService::Self();
    2293           0 :     if (storageService) {
    2294           0 :       nsAutoCString idExtension, url;
    2295             :       nsCOMPtr<nsILoadContextInfo> info =
    2296           0 :         CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
    2297           0 :       MOZ_ASSERT(info);
    2298           0 :       if (info) {
    2299           0 :         storageService->CacheFileDoomed(info, idExtension, url);
    2300             :       }
    2301             :     }
    2302             :   }
    2303             : 
    2304           0 :   return NS_OK;
    2305             : }
    2306             : 
    2307             : // static
    2308             : nsresult
    2309           1 : CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
    2310             :                                   CacheFileIOListener *aCallback)
    2311             : {
    2312           1 :   LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
    2313             :        PromiseFlatCString(aKey).get(), aCallback));
    2314             : 
    2315             :   nsresult rv;
    2316           2 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2317             : 
    2318           1 :   if (!ioMan) {
    2319           0 :     return NS_ERROR_NOT_INITIALIZED;
    2320             :   }
    2321             : 
    2322           2 :   RefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
    2323           1 :   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
    2324           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2325             : 
    2326           1 :   return NS_OK;
    2327             : }
    2328             : 
    2329             : nsresult
    2330           1 : CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
    2331             : {
    2332           1 :   LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
    2333             :        , LOGSHA1(aHash)));
    2334             : 
    2335           1 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    2336             : 
    2337             :   nsresult rv;
    2338             : 
    2339           1 :   if (mShuttingDown) {
    2340           0 :     return NS_ERROR_NOT_INITIALIZED;
    2341             :   }
    2342             : 
    2343           1 :   if (!mCacheDirectory) {
    2344           0 :     return NS_ERROR_FILE_INVALID_PATH;
    2345             :   }
    2346             : 
    2347             :   // Find active handle
    2348           2 :   RefPtr<CacheFileHandle> handle;
    2349           1 :   mHandles.GetHandle(aHash, getter_AddRefs(handle));
    2350             : 
    2351           1 :   if (handle) {
    2352           0 :     handle->Log();
    2353             : 
    2354           0 :     return DoomFileInternal(handle);
    2355             :   }
    2356             : 
    2357           2 :   CacheIOThread::Cancelable cancelable(true);
    2358             : 
    2359             :   // There is no handle for this file, delete the file if exists
    2360           2 :   nsCOMPtr<nsIFile> file;
    2361           1 :   rv = GetFile(aHash, getter_AddRefs(file));
    2362           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2363             : 
    2364             :   bool exists;
    2365           1 :   rv = file->Exists(&exists);
    2366           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2367             : 
    2368           1 :   if (!exists) {
    2369           1 :     return NS_ERROR_NOT_AVAILABLE;
    2370             :   }
    2371             : 
    2372           0 :   LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
    2373             :        "disk"));
    2374           0 :   rv = file->Remove(false);
    2375           0 :   if (NS_FAILED(rv)) {
    2376           0 :     NS_WARNING("Cannot remove old entry from the disk");
    2377           0 :     LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
    2378             :          "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    2379             :   }
    2380             : 
    2381           0 :   CacheIndex::RemoveEntry(aHash);
    2382             : 
    2383           0 :   return NS_OK;
    2384             : }
    2385             : 
    2386             : // static
    2387             : nsresult
    2388           4 : CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
    2389             : {
    2390           4 :   LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
    2391             : 
    2392             :   nsresult rv;
    2393           8 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2394             : 
    2395           4 :   if (aHandle->IsClosed() || !ioMan) {
    2396           0 :     return NS_ERROR_NOT_INITIALIZED;
    2397             :   }
    2398             : 
    2399           8 :   RefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
    2400           8 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    2401             :                                   ? CacheIOThread::WRITE_PRIORITY
    2402           8 :                                   : CacheIOThread::WRITE);
    2403           4 :   NS_ENSURE_SUCCESS(rv, rv);
    2404             : 
    2405           4 :   return NS_OK;
    2406             : }
    2407             : 
    2408             : nsresult
    2409           4 : CacheFileIOManager::MaybeReleaseNSPRHandleInternal(CacheFileHandle *aHandle,
    2410             :                                                    bool aIgnoreShutdownLag)
    2411             : {
    2412           4 :   LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() [handle=%p, ignore shutdown=%d]",
    2413             :        aHandle, aIgnoreShutdownLag));
    2414             : 
    2415           4 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    2416             : 
    2417           4 :   if (aHandle->mFD) {
    2418           8 :     DebugOnly<bool> found;
    2419           4 :     found = mHandlesByLastUsed.RemoveElement(aHandle);
    2420           4 :     MOZ_ASSERT(found);
    2421             :   }
    2422             : 
    2423           4 :   PRFileDesc *fd = aHandle->mFD;
    2424           4 :   aHandle->mFD = nullptr;
    2425             : 
    2426             :   // Leak invalid (w/o metadata) and doomed handles immediately after shutdown.
    2427             :   // Leak other handles when past the shutdown time maximum lag.
    2428           8 :   if (
    2429             : #ifndef DEBUG
    2430             :       ((aHandle->mInvalid || aHandle->mIsDoomed) &&
    2431             :       MOZ_UNLIKELY(CacheObserver::ShuttingDown())) ||
    2432             : #endif
    2433           8 :       MOZ_UNLIKELY(!aIgnoreShutdownLag &&
    2434             :                    CacheObserver::IsPastShutdownIOLag())) {
    2435             :     // Don't bother closing this file.  Return a failure code from here will
    2436             :     // cause any following IO operation on the file (mainly removal) to be
    2437             :     // bypassed, which is what we want.
    2438             :     // For mInvalid == true the entry will never be used, since it doesn't
    2439             :     // have correct metadata, thus we don't need to worry about removing it.
    2440             :     // For mIsDoomed == true the file is already in the doomed sub-dir and
    2441             :     // will be removed on next session start.
    2442           0 :     LOG(("  past the shutdown I/O lag, leaking file handle"));
    2443           0 :     return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
    2444             :   }
    2445             : 
    2446           4 :   if (!fd) {
    2447             :     // The filedesc has already been closed before, just let go.
    2448           0 :     return NS_OK;
    2449             :   }
    2450             : 
    2451           8 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    2452             : 
    2453           4 :   PRStatus status = PR_Close(fd);
    2454           4 :   if (status != PR_SUCCESS) {
    2455           0 :     LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() "
    2456             :          "failed to close [handle=%p, status=%u]", aHandle, status));
    2457           0 :     return NS_ERROR_FAILURE;
    2458             :   }
    2459             : 
    2460           4 :   LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() DONE"));
    2461             : 
    2462           4 :   return NS_OK;
    2463             : }
    2464             : 
    2465             : // static
    2466             : nsresult
    2467           0 : CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
    2468             :                                        int64_t aTruncatePos, int64_t aEOFPos,
    2469             :                                        CacheFileIOListener *aCallback)
    2470             : {
    2471           0 :   LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%" PRId64 ", "
    2472             :        "EOFPos=%" PRId64 ", listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
    2473             : 
    2474             :   nsresult rv;
    2475           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2476             : 
    2477           0 :   if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) {
    2478           0 :     return NS_ERROR_NOT_INITIALIZED;
    2479             :   }
    2480             : 
    2481             :   RefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
    2482             :                                            aHandle, aTruncatePos, aEOFPos,
    2483           0 :                                            aCallback);
    2484           0 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    2485             :                                   ? CacheIOThread::WRITE_PRIORITY
    2486           0 :                                   : CacheIOThread::WRITE);
    2487           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2488             : 
    2489           0 :   return NS_OK;
    2490             : }
    2491             : 
    2492             : // static
    2493           1 : void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
    2494             : {
    2495           1 :   *result = nullptr;
    2496             : 
    2497           2 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2498           1 :   if (!ioMan || !ioMan->mCacheDirectory) {
    2499           0 :     return;
    2500             :   }
    2501             : 
    2502           1 :   ioMan->mCacheDirectory->Clone(result);
    2503             : }
    2504             : 
    2505             : #if defined(MOZ_WIDGET_ANDROID)
    2506             : 
    2507             : // static
    2508             : void CacheFileIOManager::GetProfilelessCacheDirectory(nsIFile** result)
    2509             : {
    2510             :   *result = nullptr;
    2511             : 
    2512             :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2513             :   if (!ioMan || !ioMan->mCacheProfilelessDirectory) {
    2514             :     return;
    2515             :   }
    2516             : 
    2517             :   ioMan->mCacheProfilelessDirectory->Clone(result);
    2518             : }
    2519             : 
    2520             : #endif
    2521             : 
    2522             : // static
    2523             : nsresult
    2524           0 : CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash,
    2525             :                                  CacheStorageService::EntryInfoCallback *aCallback)
    2526             : {
    2527           0 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
    2528             : 
    2529             :   nsresult rv;
    2530             : 
    2531           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2532           0 :   if (!ioMan) {
    2533           0 :     return NS_ERROR_NOT_INITIALIZED;
    2534             :   }
    2535             : 
    2536           0 :   nsAutoCString enhanceId;
    2537           0 :   nsAutoCString uriSpec;
    2538             : 
    2539           0 :   RefPtr<CacheFileHandle> handle;
    2540           0 :   ioMan->mHandles.GetHandle(aHash, getter_AddRefs(handle));
    2541           0 :   if (handle) {
    2542             :     RefPtr<nsILoadContextInfo> info =
    2543           0 :       CacheFileUtils::ParseKey(handle->Key(), &enhanceId, &uriSpec);
    2544             : 
    2545           0 :     MOZ_ASSERT(info);
    2546           0 :     if (!info) {
    2547           0 :       return NS_OK; // ignore
    2548             :     }
    2549             : 
    2550           0 :     RefPtr<CacheStorageService> service = CacheStorageService::Self();
    2551           0 :     if (!service) {
    2552           0 :       return NS_ERROR_NOT_INITIALIZED;
    2553             :     }
    2554             : 
    2555             :     // Invokes OnCacheEntryInfo when an existing entry is found
    2556           0 :     if (service->GetCacheEntryInfo(info, enhanceId, uriSpec, aCallback)) {
    2557           0 :       return NS_OK;
    2558             :     }
    2559             : 
    2560             :     // When we are here, there is no existing entry and we need
    2561             :     // to synchrnously load metadata from a disk file.
    2562             :   }
    2563             : 
    2564             :   // Locate the actual file
    2565           0 :   nsCOMPtr<nsIFile> file;
    2566           0 :   ioMan->GetFile(aHash, getter_AddRefs(file));
    2567             : 
    2568             :   // Read metadata from the file synchronously
    2569           0 :   RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata();
    2570           0 :   rv = metadata->SyncReadMetadata(file);
    2571           0 :   if (NS_FAILED(rv)) {
    2572           0 :     return NS_OK;
    2573             :   }
    2574             : 
    2575             :   // Now get the context + enhance id + URL from the key.
    2576           0 :   nsAutoCString key;
    2577           0 :   metadata->GetKey(key);
    2578             : 
    2579             :   RefPtr<nsILoadContextInfo> info =
    2580           0 :     CacheFileUtils::ParseKey(key, &enhanceId, &uriSpec);
    2581           0 :   MOZ_ASSERT(info);
    2582           0 :   if (!info) {
    2583           0 :     return NS_OK;
    2584             :   }
    2585             : 
    2586             :   // Pick all data to pass to the callback.
    2587           0 :   int64_t dataSize = metadata->Offset();
    2588             :   uint32_t fetchCount;
    2589           0 :   if (NS_FAILED(metadata->GetFetchCount(&fetchCount))) {
    2590           0 :     fetchCount = 0;
    2591             :   }
    2592             :   uint32_t expirationTime;
    2593           0 :   if (NS_FAILED(metadata->GetExpirationTime(&expirationTime))) {
    2594           0 :     expirationTime = 0;
    2595             :   }
    2596             :   uint32_t lastModified;
    2597           0 :   if (NS_FAILED(metadata->GetLastModified(&lastModified))) {
    2598           0 :     lastModified = 0;
    2599             :   }
    2600             : 
    2601             :   // Call directly on the callback.
    2602           0 :   aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize, fetchCount,
    2603           0 :                          lastModified, expirationTime, metadata->Pinned(),
    2604           0 :                          info);
    2605             : 
    2606           0 :   return NS_OK;
    2607             : }
    2608             : 
    2609             : nsresult
    2610           0 : CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
    2611             :                                                int64_t aTruncatePos,
    2612             :                                                int64_t aEOFPos)
    2613             : {
    2614           0 :   LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
    2615             :        "truncatePos=%" PRId64 ", EOFPos=%" PRId64 "]", aHandle, aTruncatePos, aEOFPos));
    2616             : 
    2617             :   nsresult rv;
    2618             : 
    2619           0 :   if (aHandle->mKilled) {
    2620           0 :     LOG(("  handle already killed, file not truncated"));
    2621           0 :     return NS_OK;
    2622             :   }
    2623             : 
    2624           0 :   if (CacheObserver::ShuttingDown() && !aHandle->mFD) {
    2625           0 :     aHandle->mKilled = true;
    2626           0 :     LOG(("  killing the handle, file not truncated"));
    2627           0 :     return NS_OK;
    2628             :   }
    2629             : 
    2630           0 :   CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
    2631             : 
    2632           0 :   if (!aHandle->mFileExists) {
    2633           0 :     rv = CreateFile(aHandle);
    2634           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2635             :   }
    2636             : 
    2637           0 :   if (!aHandle->mFD) {
    2638           0 :     rv = OpenNSPRHandle(aHandle);
    2639           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2640             :   } else {
    2641           0 :     NSPRHandleUsed(aHandle);
    2642             :   }
    2643             : 
    2644             :   // Check again, OpenNSPRHandle could figure out the file was gone.
    2645           0 :   if (!aHandle->mFileExists) {
    2646           0 :     return NS_ERROR_NOT_AVAILABLE;
    2647             :   }
    2648             : 
    2649             :   // When this operation would increase cache size, check whether the cache size
    2650             :   // reached the hard limit and whether it would cause critical low disk space.
    2651           0 :   if (aHandle->mFileSize < aEOFPos) {
    2652           0 :     if (mOverLimitEvicting && mCacheSizeOnHardLimit) {
    2653           0 :       LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - failing because "
    2654             :            "cache size reached hard limit!"));
    2655           0 :       return NS_ERROR_FILE_DISK_FULL;
    2656             :     }
    2657             : 
    2658           0 :     int64_t freeSpace = -1;
    2659           0 :     rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
    2660           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2661           0 :       LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - "
    2662             :            "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
    2663             :            static_cast<uint32_t>(rv)));
    2664             :     } else {
    2665           0 :       uint32_t limit = CacheObserver::DiskFreeSpaceHardLimit();
    2666           0 :       if (freeSpace - aEOFPos + aHandle->mFileSize < limit) {
    2667           0 :         LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - Low free space"
    2668             :              ", refusing to write! [freeSpace=%" PRId64 ", limit=%u]", freeSpace,
    2669             :              limit));
    2670           0 :         return NS_ERROR_FILE_DISK_FULL;
    2671             :       }
    2672             :     }
    2673             :   }
    2674             : 
    2675             :   // This operation always invalidates the entry
    2676           0 :   aHandle->mInvalid = true;
    2677             : 
    2678           0 :   rv = TruncFile(aHandle->mFD, aTruncatePos);
    2679           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2680             : 
    2681           0 :   if (aTruncatePos != aEOFPos) {
    2682           0 :     rv = TruncFile(aHandle->mFD, aEOFPos);
    2683           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2684             :   }
    2685             : 
    2686           0 :   uint32_t oldSizeInK = aHandle->FileSizeInK();
    2687           0 :   aHandle->mFileSize = aEOFPos;
    2688           0 :   uint32_t newSizeInK = aHandle->FileSizeInK();
    2689             : 
    2690           0 :   if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
    2691           0 :       !aHandle->IsSpecialFile()) {
    2692           0 :     CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
    2693           0 :                             nullptr, &newSizeInK);
    2694             : 
    2695           0 :     if (oldSizeInK < newSizeInK) {
    2696           0 :       EvictIfOverLimitInternal();
    2697             :     }
    2698             :   }
    2699             : 
    2700           0 :   return NS_OK;
    2701             : }
    2702             : 
    2703             : // static
    2704             : nsresult
    2705           0 : CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
    2706             :                                const nsACString &aNewName,
    2707             :                                CacheFileIOListener *aCallback)
    2708             : {
    2709           0 :   LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
    2710             :        aHandle, PromiseFlatCString(aNewName).get(), aCallback));
    2711             : 
    2712             :   nsresult rv;
    2713           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2714             : 
    2715           0 :   if (aHandle->IsClosed() || !ioMan) {
    2716           0 :     return NS_ERROR_NOT_INITIALIZED;
    2717             :   }
    2718             : 
    2719           0 :   if (!aHandle->IsSpecialFile()) {
    2720           0 :     return NS_ERROR_UNEXPECTED;
    2721             :   }
    2722             : 
    2723             :   RefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
    2724           0 :                                                      aCallback);
    2725           0 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    2726             :                                   ? CacheIOThread::WRITE_PRIORITY
    2727           0 :                                   : CacheIOThread::WRITE);
    2728           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2729             : 
    2730           0 :   return NS_OK;
    2731             : }
    2732             : 
    2733             : nsresult
    2734           0 : CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
    2735             :                                        const nsACString &aNewName)
    2736             : {
    2737           0 :   LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
    2738             :        aHandle, PromiseFlatCString(aNewName).get()));
    2739             : 
    2740             :   nsresult rv;
    2741             : 
    2742           0 :   MOZ_ASSERT(aHandle->IsSpecialFile());
    2743             : 
    2744           0 :   if (aHandle->IsDoomed()) {
    2745           0 :     return NS_ERROR_NOT_AVAILABLE;
    2746             :   }
    2747             : 
    2748             :   // Doom old handle if it exists and is not doomed
    2749           0 :   for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
    2750           0 :     if (!mSpecialHandles[i]->IsDoomed() &&
    2751           0 :         mSpecialHandles[i]->Key() == aNewName) {
    2752           0 :       MOZ_ASSERT(aHandle != mSpecialHandles[i]);
    2753           0 :       rv = DoomFileInternal(mSpecialHandles[i]);
    2754           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2755           0 :       break;
    2756             :     }
    2757             :   }
    2758             : 
    2759           0 :   nsCOMPtr<nsIFile> file;
    2760           0 :   rv = GetSpecialFile(aNewName, getter_AddRefs(file));
    2761           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2762             : 
    2763             :   bool exists;
    2764           0 :   rv = file->Exists(&exists);
    2765           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2766             : 
    2767           0 :   if (exists) {
    2768           0 :     LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
    2769             :          "disk"));
    2770           0 :     rv = file->Remove(false);
    2771           0 :     if (NS_FAILED(rv)) {
    2772           0 :       NS_WARNING("Cannot remove file from the disk");
    2773           0 :       LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
    2774             :            ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    2775             :     }
    2776             :   }
    2777             : 
    2778           0 :   if (!aHandle->FileExists()) {
    2779           0 :     aHandle->mKey = aNewName;
    2780           0 :     return NS_OK;
    2781             :   }
    2782             : 
    2783           0 :   rv = MaybeReleaseNSPRHandleInternal(aHandle, true);
    2784           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2785             : 
    2786           0 :   rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
    2787           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2788             : 
    2789           0 :   aHandle->mKey = aNewName;
    2790           0 :   return NS_OK;
    2791             : }
    2792             : 
    2793             : // static
    2794             : nsresult
    2795           1 : CacheFileIOManager::EvictIfOverLimit()
    2796             : {
    2797           1 :   LOG(("CacheFileIOManager::EvictIfOverLimit()"));
    2798             : 
    2799             :   nsresult rv;
    2800           2 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    2801             : 
    2802           1 :   if (!ioMan) {
    2803           0 :     return NS_ERROR_NOT_INITIALIZED;
    2804             :   }
    2805             : 
    2806           2 :   nsCOMPtr<nsIRunnable> ev;
    2807           2 :   ev = NewRunnableMethod("net::CacheFileIOManager::EvictIfOverLimitInternal",
    2808             :                          ioMan,
    2809           1 :                          &CacheFileIOManager::EvictIfOverLimitInternal);
    2810             : 
    2811           1 :   rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
    2812           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2813             : 
    2814           1 :   return NS_OK;
    2815             : }
    2816             : 
    2817             : nsresult
    2818           4 : CacheFileIOManager::EvictIfOverLimitInternal()
    2819             : {
    2820           4 :   LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
    2821             : 
    2822             :   nsresult rv;
    2823             : 
    2824           4 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    2825             : 
    2826           4 :   if (mShuttingDown) {
    2827           0 :     return NS_ERROR_NOT_INITIALIZED;
    2828             :   }
    2829             : 
    2830           4 :   if (mOverLimitEvicting) {
    2831           0 :     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
    2832             :          "running."));
    2833           0 :     return NS_OK;
    2834             :   }
    2835             : 
    2836           8 :   CacheIOThread::Cancelable cancelable(true);
    2837             : 
    2838             :   int64_t freeSpace;
    2839           4 :   rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
    2840           4 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2841           0 :     freeSpace = -1;
    2842             : 
    2843             :     // Do not change smart size.
    2844           0 :     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
    2845             :          "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
    2846             :          static_cast<uint32_t>(rv)));
    2847             :   } else {
    2848           4 :     UpdateSmartCacheSize(freeSpace);
    2849             :   }
    2850             : 
    2851             :   uint32_t cacheUsage;
    2852           4 :   rv = CacheIndex::GetCacheSize(&cacheUsage);
    2853           4 :   NS_ENSURE_SUCCESS(rv, rv);
    2854             : 
    2855           4 :   uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
    2856           4 :   uint32_t freeSpaceLimit = CacheObserver::DiskFreeSpaceSoftLimit();
    2857             : 
    2858           8 :   if (cacheUsage <= cacheLimit &&
    2859           8 :       (freeSpace == -1 || freeSpace >= freeSpaceLimit)) {
    2860           4 :     LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size and free "
    2861             :          "space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
    2862             :          "freeSpace=%" PRId64 ", freeSpaceLimit=%u]", cacheUsage, cacheLimit,
    2863             :          freeSpace, freeSpaceLimit));
    2864           4 :     return NS_OK;
    2865             :   }
    2866             : 
    2867           0 :   LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
    2868             :        "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
    2869             :        cacheUsage, cacheLimit));
    2870             : 
    2871           0 :   nsCOMPtr<nsIRunnable> ev;
    2872           0 :   ev = NewRunnableMethod("net::CacheFileIOManager::OverLimitEvictionInternal",
    2873             :                          this,
    2874           0 :                          &CacheFileIOManager::OverLimitEvictionInternal);
    2875             : 
    2876           0 :   rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
    2877           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2878             : 
    2879           0 :   mOverLimitEvicting = true;
    2880           0 :   return NS_OK;
    2881             : }
    2882             : 
    2883             : nsresult
    2884           0 : CacheFileIOManager::OverLimitEvictionInternal()
    2885             : {
    2886           0 :   LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
    2887             : 
    2888             :   nsresult rv;
    2889             : 
    2890           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    2891             : 
    2892             :   // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
    2893             :   // here and set it to true again once we dispatch another event that will
    2894             :   // continue with the eviction. The reason why we do so is that we can fail
    2895             :   // early anywhere in this method and the variable will contain a correct
    2896             :   // value. Otherwise we would need to set it to false on every failing place.
    2897           0 :   mOverLimitEvicting = false;
    2898             : 
    2899           0 :   if (mShuttingDown) {
    2900           0 :     return NS_ERROR_NOT_INITIALIZED;
    2901             :   }
    2902             : 
    2903             :   while (true) {
    2904           0 :     int64_t freeSpace = -1;
    2905           0 :     rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
    2906           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2907             :       // Do not change smart size.
    2908           0 :       LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
    2909             :            "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
    2910             :            static_cast<uint32_t>(rv)));
    2911             :     } else {
    2912           0 :       UpdateSmartCacheSize(freeSpace);
    2913             :     }
    2914             : 
    2915             :     uint32_t cacheUsage;
    2916           0 :     rv = CacheIndex::GetCacheSize(&cacheUsage);
    2917           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2918             : 
    2919           0 :     uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
    2920           0 :     uint32_t freeSpaceLimit = CacheObserver::DiskFreeSpaceSoftLimit();
    2921             : 
    2922           0 :     if (cacheUsage > cacheLimit) {
    2923           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
    2924             :            "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
    2925             : 
    2926             :       // We allow cache size to go over the specified limit. Eviction should
    2927             :       // keep the size within the limit in a long run, but in case the eviction
    2928             :       // is too slow, the cache could go way over the limit. To prevent this we
    2929             :       // set flag mCacheSizeOnHardLimit when the size reaches 105% of the limit
    2930             :       // and WriteInternal() and TruncateSeekSetEOFInternal() fail to cache
    2931             :       // additional data.
    2932           0 :       if ((cacheUsage - cacheLimit) > (cacheLimit / 20)) {
    2933           0 :         LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size "
    2934             :              "reached hard limit."));
    2935           0 :         mCacheSizeOnHardLimit = true;
    2936             :       } else {
    2937           0 :         mCacheSizeOnHardLimit = false;
    2938             :       }
    2939           0 :     } else if (freeSpace != 1 && freeSpace < freeSpaceLimit) {
    2940           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Free space under "
    2941             :            "limit. [freeSpace=%" PRId64 ", freeSpaceLimit=%u]", freeSpace,
    2942             :            freeSpaceLimit));
    2943             :     } else {
    2944           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size and "
    2945             :            "free space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
    2946             :            "freeSpace=%" PRId64 ", freeSpaceLimit=%u]", cacheUsage, cacheLimit,
    2947             :            freeSpace, freeSpaceLimit));
    2948             : 
    2949           0 :       mCacheSizeOnHardLimit = false;
    2950           0 :       return NS_OK;
    2951             :     }
    2952             : 
    2953           0 :     if (CacheIOThread::YieldAndRerun()) {
    2954           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
    2955             :            "for higher level events."));
    2956           0 :       mOverLimitEvicting = true;
    2957           0 :       return NS_OK;
    2958             :     }
    2959             : 
    2960             :     SHA1Sum::Hash hash;
    2961             :     uint32_t cnt;
    2962             :     static uint32_t consecutiveFailures = 0;
    2963           0 :     rv = CacheIndex::GetEntryForEviction(false, &hash, &cnt);
    2964           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2965             : 
    2966           0 :     rv = DoomFileByKeyInternal(&hash);
    2967           0 :     if (NS_SUCCEEDED(rv)) {
    2968           0 :       consecutiveFailures = 0;
    2969           0 :     } else if (rv == NS_ERROR_NOT_AVAILABLE) {
    2970           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
    2971             :            "DoomFileByKeyInternal() failed. [rv=0x%08" PRIx32 "]",
    2972             :            static_cast<uint32_t>(rv)));
    2973             :       // TODO index is outdated, start update
    2974             : 
    2975             :       // Make sure index won't return the same entry again
    2976           0 :       CacheIndex::RemoveEntry(&hash);
    2977           0 :       consecutiveFailures = 0;
    2978             :     } else {
    2979             :       // This shouldn't normally happen, but the eviction must not fail
    2980             :       // completely if we ever encounter this problem.
    2981             :       NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
    2982           0 :                  "failure of DoomFileByKeyInternal()");
    2983             : 
    2984           0 :       LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
    2985             :            "DoomFileByKeyInternal() failed. [rv=0x%08" PRIx32 "]",
    2986             :            static_cast<uint32_t>(rv)));
    2987             : 
    2988             :       // Normally, CacheIndex::UpdateEntry() is called only to update newly
    2989             :       // created/opened entries which are always fresh and UpdateEntry() expects
    2990             :       // and checks this flag. The way we use UpdateEntry() here is a kind of
    2991             :       // hack and we must make sure the flag is set by calling
    2992             :       // EnsureEntryExists().
    2993           0 :       rv = CacheIndex::EnsureEntryExists(&hash);
    2994           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2995             : 
    2996             :       // Move the entry at the end of both lists to make sure we won't end up
    2997             :       // failing on one entry forever.
    2998           0 :       uint32_t frecency = 0;
    2999           0 :       uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
    3000             :       rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr, nullptr,
    3001           0 :                                    nullptr, nullptr);
    3002           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3003             : 
    3004           0 :       consecutiveFailures++;
    3005           0 :       if (consecutiveFailures >= cnt) {
    3006             :         // This doesn't necessarily mean that we've tried to doom every entry
    3007             :         // but we've reached a sane number of tries. It is likely that another
    3008             :         // eviction will start soon. And as said earlier, this normally doesn't
    3009             :         // happen at all.
    3010           0 :         return NS_OK;
    3011             :       }
    3012             :     }
    3013           0 :   }
    3014             : 
    3015             :   NS_NOTREACHED("We should never get here");
    3016             :   return NS_OK;
    3017             : }
    3018             : 
    3019             : // static
    3020             : nsresult
    3021           0 : CacheFileIOManager::EvictAll()
    3022             : {
    3023           0 :   LOG(("CacheFileIOManager::EvictAll()"));
    3024             : 
    3025             :   nsresult rv;
    3026           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    3027             : 
    3028           0 :   if (!ioMan) {
    3029           0 :     return NS_ERROR_NOT_INITIALIZED;
    3030             :   }
    3031             : 
    3032           0 :   nsCOMPtr<nsIRunnable> ev;
    3033           0 :   ev = NewRunnableMethod("net::CacheFileIOManager::EvictAllInternal",
    3034             :                          ioMan,
    3035           0 :                          &CacheFileIOManager::EvictAllInternal);
    3036             : 
    3037           0 :   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
    3038           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3039           0 :     return rv;
    3040             :   }
    3041             : 
    3042           0 :   return NS_OK;
    3043             : }
    3044             : 
    3045             : namespace {
    3046             : 
    3047           0 : class EvictionNotifierRunnable : public Runnable
    3048             : {
    3049             : public:
    3050           0 :   EvictionNotifierRunnable() : Runnable("EvictionNotifierRunnable") {}
    3051             :   NS_DECL_NSIRUNNABLE
    3052             : };
    3053             : 
    3054             : NS_IMETHODIMP
    3055           0 : EvictionNotifierRunnable::Run()
    3056             : {
    3057           0 :   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
    3058           0 :   if (obsSvc) {
    3059           0 :     obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
    3060             :   }
    3061           0 :   return NS_OK;
    3062             : }
    3063             : 
    3064             : } // namespace
    3065             : 
    3066             : nsresult
    3067           0 : CacheFileIOManager::EvictAllInternal()
    3068             : {
    3069           0 :   LOG(("CacheFileIOManager::EvictAllInternal()"));
    3070             : 
    3071             :   nsresult rv;
    3072             : 
    3073           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3074             : 
    3075           0 :   RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
    3076             : 
    3077           0 :   if (!mCacheDirectory) {
    3078             :     // This is a kind of hack. Somebody called EvictAll() without a profile.
    3079             :     // This happens in xpcshell tests that use cache without profile. We need
    3080             :     // to notify observers in this case since the tests are waiting for it.
    3081           0 :     NS_DispatchToMainThread(r);
    3082           0 :     return NS_ERROR_FILE_INVALID_PATH;
    3083             :   }
    3084             : 
    3085           0 :   if (mShuttingDown) {
    3086           0 :     return NS_ERROR_NOT_INITIALIZED;
    3087             :   }
    3088             : 
    3089           0 :   if (!mTreeCreated) {
    3090           0 :     rv = CreateCacheTree();
    3091           0 :     if (NS_FAILED(rv)) {
    3092           0 :       return rv;
    3093             :     }
    3094             :   }
    3095             : 
    3096             :   // Doom all active handles
    3097           0 :   nsTArray<RefPtr<CacheFileHandle> > handles;
    3098           0 :   mHandles.GetActiveHandles(&handles);
    3099             : 
    3100           0 :   for (uint32_t i = 0; i < handles.Length(); ++i) {
    3101           0 :     rv = DoomFileInternal(handles[i]);
    3102           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3103           0 :       LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
    3104             :            "[handle=%p]", handles[i].get()));
    3105             :     }
    3106             :   }
    3107             : 
    3108           0 :   nsCOMPtr<nsIFile> file;
    3109           0 :   rv = mCacheDirectory->Clone(getter_AddRefs(file));
    3110           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3111           0 :     return rv;
    3112             :   }
    3113             : 
    3114           0 :   rv = file->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR));
    3115           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3116           0 :     return rv;
    3117             :   }
    3118             : 
    3119             :   // Trash current entries directory
    3120           0 :   rv = TrashDirectory(file);
    3121           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3122           0 :     return rv;
    3123             :   }
    3124             : 
    3125             :   // Files are now inaccessible in entries directory, notify observers.
    3126           0 :   NS_DispatchToMainThread(r);
    3127             : 
    3128             :   // Create a new empty entries directory
    3129           0 :   rv = CheckAndCreateDir(mCacheDirectory, ENTRIES_DIR, false);
    3130           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3131           0 :     return rv;
    3132             :   }
    3133             : 
    3134           0 :   CacheIndex::RemoveAll();
    3135             : 
    3136           0 :   return NS_OK;
    3137             : }
    3138             : 
    3139             : // static
    3140             : nsresult
    3141           0 : CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
    3142             : {
    3143           0 :   LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
    3144             :        aLoadContextInfo));
    3145             : 
    3146             :   nsresult rv;
    3147           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    3148             : 
    3149           0 :   if (!ioMan) {
    3150           0 :     return NS_ERROR_NOT_INITIALIZED;
    3151             :   }
    3152             : 
    3153           0 :   nsCOMPtr<nsIRunnable> ev;
    3154           0 :   ev = NewRunnableMethod<nsCOMPtr<nsILoadContextInfo>, bool>(
    3155             :     "net::CacheFileIOManager::EvictByContextInternal",
    3156             :     ioMan,
    3157             :     &CacheFileIOManager::EvictByContextInternal,
    3158             :     aLoadContextInfo,
    3159           0 :     aPinned);
    3160             : 
    3161           0 :   rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
    3162           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3163           0 :     return rv;
    3164             :   }
    3165             : 
    3166           0 :   return NS_OK;
    3167             : }
    3168             : 
    3169             : nsresult
    3170           0 : CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
    3171             : {
    3172           0 :   LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, pinned=%d]",
    3173             :       aLoadContextInfo, aPinned));
    3174             : 
    3175             :   nsresult rv;
    3176             : 
    3177           0 :   if (aLoadContextInfo) {
    3178           0 :     nsAutoCString suffix;
    3179           0 :     aLoadContextInfo->OriginAttributesPtr()->CreateSuffix(suffix);
    3180           0 :     LOG(("  anonymous=%u, suffix=%s]", aLoadContextInfo->IsAnonymous(), suffix.get()));
    3181             : 
    3182           0 :     MOZ_ASSERT(mIOThread->IsCurrentThread());
    3183             : 
    3184           0 :     MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
    3185           0 :     if (aLoadContextInfo->IsPrivate()) {
    3186           0 :       return NS_ERROR_INVALID_ARG;
    3187             :     }
    3188             :   }
    3189             : 
    3190           0 :   if (!mCacheDirectory) {
    3191             :     // This is a kind of hack. Somebody called EvictAll() without a profile.
    3192             :     // This happens in xpcshell tests that use cache without profile. We need
    3193             :     // to notify observers in this case since the tests are waiting for it.
    3194             :     // Also notify for aPinned == true, those are interested as well.
    3195           0 :     if (!aLoadContextInfo) {
    3196           0 :       RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
    3197           0 :       NS_DispatchToMainThread(r);
    3198             :     }
    3199           0 :     return NS_ERROR_FILE_INVALID_PATH;
    3200             :   }
    3201             : 
    3202           0 :   if (mShuttingDown) {
    3203           0 :     return NS_ERROR_NOT_INITIALIZED;
    3204             :   }
    3205             : 
    3206           0 :   if (!mTreeCreated) {
    3207           0 :     rv = CreateCacheTree();
    3208           0 :     if (NS_FAILED(rv)) {
    3209           0 :       return rv;
    3210             :     }
    3211             :   }
    3212             : 
    3213             :   // Doom all active handles that matches the load context
    3214           0 :   nsTArray<RefPtr<CacheFileHandle> > handles;
    3215           0 :   mHandles.GetActiveHandles(&handles);
    3216             : 
    3217           0 :   for (uint32_t i = 0; i < handles.Length(); ++i) {
    3218           0 :     CacheFileHandle* handle = handles[i];
    3219             : 
    3220           0 :     if (aLoadContextInfo) {
    3221             :       bool equals;
    3222           0 :       rv = CacheFileUtils::KeyMatchesLoadContextInfo(handle->Key(),
    3223             :                                                      aLoadContextInfo,
    3224           0 :                                                      &equals);
    3225           0 :       if (NS_FAILED(rv)) {
    3226           0 :         LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
    3227             :              "handle! [handle=%p, key=%s]", handle, handle->Key().get()));
    3228           0 :         MOZ_CRASH("Unexpected error!");
    3229             :       }
    3230             : 
    3231           0 :       if (!equals) {
    3232           0 :         continue;
    3233             :       }
    3234             :     }
    3235             : 
    3236             :     // handle will be doomed only when pinning status is known and equal or
    3237             :     // doom decision will be deferred until pinning status is determined.
    3238           0 :     rv = DoomFileInternal(handle, aPinned
    3239             :                                   ? CacheFileIOManager::DOOM_WHEN_PINNED
    3240           0 :                                   : CacheFileIOManager::DOOM_WHEN_NON_PINNED);
    3241           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3242           0 :       LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
    3243             :             " [handle=%p]", handle));
    3244             :     }
    3245             :   }
    3246             : 
    3247           0 :   if (!aLoadContextInfo) {
    3248           0 :     RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
    3249           0 :     NS_DispatchToMainThread(r);
    3250             :   }
    3251             : 
    3252           0 :   if (!mContextEvictor) {
    3253           0 :     mContextEvictor = new CacheFileContextEvictor();
    3254           0 :     mContextEvictor->Init(mCacheDirectory);
    3255             :   }
    3256             : 
    3257           0 :   mContextEvictor->AddContext(aLoadContextInfo, aPinned);
    3258             : 
    3259           0 :   return NS_OK;
    3260             : }
    3261             : 
    3262             : // static
    3263             : nsresult
    3264           2 : CacheFileIOManager::CacheIndexStateChanged()
    3265             : {
    3266           2 :   LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
    3267             : 
    3268             :   nsresult rv;
    3269             : 
    3270             :   // CacheFileIOManager lives longer than CacheIndex so gInstance must be
    3271             :   // non-null here.
    3272           2 :   MOZ_ASSERT(gInstance);
    3273             : 
    3274             :   // We have to re-distatch even if we are on IO thread to prevent reentering
    3275             :   // the lock in CacheIndex
    3276           4 :   nsCOMPtr<nsIRunnable> ev;
    3277             :   ev =
    3278           6 :     NewRunnableMethod("net::CacheFileIOManager::CacheIndexStateChangedInternal",
    3279           4 :                       gInstance.get(),
    3280           2 :                       &CacheFileIOManager::CacheIndexStateChangedInternal);
    3281             : 
    3282           4 :   nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
    3283           2 :   MOZ_ASSERT(ioTarget);
    3284             : 
    3285           2 :   rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
    3286           2 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3287           0 :     return rv;
    3288             :   }
    3289             : 
    3290           2 :   return NS_OK;
    3291             : }
    3292             : 
    3293             : nsresult
    3294           2 : CacheFileIOManager::CacheIndexStateChangedInternal()
    3295             : {
    3296           2 :   if (mShuttingDown) {
    3297             :     // ignore notification during shutdown
    3298           0 :     return NS_OK;
    3299             :   }
    3300             : 
    3301           2 :   if (!mContextEvictor) {
    3302           2 :     return NS_OK;
    3303             :   }
    3304             : 
    3305           0 :   mContextEvictor->CacheIndexStateChanged();
    3306           0 :   return NS_OK;
    3307             : }
    3308             : 
    3309             : nsresult
    3310           0 : CacheFileIOManager::TrashDirectory(nsIFile *aFile)
    3311             : {
    3312           0 :   nsAutoCString path;
    3313           0 :   aFile->GetNativePath(path);
    3314           0 :   LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
    3315             : 
    3316             :   nsresult rv;
    3317             : 
    3318           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3319           0 :   MOZ_ASSERT(mCacheDirectory);
    3320             : 
    3321             :   // When the directory is empty, it is cheaper to remove it directly instead of
    3322             :   // using the trash mechanism.
    3323             :   bool isEmpty;
    3324           0 :   rv = IsEmptyDirectory(aFile, &isEmpty);
    3325           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3326             : 
    3327           0 :   if (isEmpty) {
    3328           0 :     rv = aFile->Remove(false);
    3329           0 :     LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08" PRIx32 "]",
    3330             :          static_cast<uint32_t>(rv)));
    3331           0 :     return rv;
    3332             :   }
    3333             : 
    3334             : #ifdef DEBUG
    3335           0 :   nsCOMPtr<nsIFile> dirCheck;
    3336           0 :   rv = aFile->GetParent(getter_AddRefs(dirCheck));
    3337           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3338             : 
    3339           0 :   bool equals = false;
    3340           0 :   rv = dirCheck->Equals(mCacheDirectory, &equals);
    3341           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3342             : 
    3343           0 :   MOZ_ASSERT(equals);
    3344             : #endif
    3345             : 
    3346           0 :   nsCOMPtr<nsIFile> dir, trash;
    3347           0 :   nsAutoCString leaf;
    3348             : 
    3349           0 :   rv = aFile->Clone(getter_AddRefs(dir));
    3350           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3351             : 
    3352           0 :   rv = aFile->Clone(getter_AddRefs(trash));
    3353           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3354             : 
    3355           0 :   const int32_t kMaxTries = 16;
    3356           0 :   srand(static_cast<unsigned>(PR_Now()));
    3357           0 :   for (int32_t triesCount = 0; ; ++triesCount) {
    3358           0 :     leaf = TRASH_DIR;
    3359           0 :     leaf.AppendInt(rand());
    3360           0 :     rv = trash->SetNativeLeafName(leaf);
    3361           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3362             : 
    3363             :     bool exists;
    3364           0 :     if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
    3365           0 :       break;
    3366             :     }
    3367             : 
    3368           0 :     LOG(("CacheFileIOManager::TrashDirectory() - Trash directory already "
    3369             :          "exists [leaf=%s]", leaf.get()));
    3370             : 
    3371           0 :     if (triesCount == kMaxTries) {
    3372           0 :       LOG(("CacheFileIOManager::TrashDirectory() - Could not find unused trash "
    3373             :            "directory in %d tries.", kMaxTries));
    3374           0 :       return NS_ERROR_FAILURE;
    3375             :     }
    3376           0 :   }
    3377             : 
    3378           0 :   LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
    3379             :        leaf.get()));
    3380             : 
    3381           0 :   rv = dir->MoveToNative(nullptr, leaf);
    3382           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3383             : 
    3384           0 :   StartRemovingTrash();
    3385           0 :   return NS_OK;
    3386             : }
    3387             : 
    3388             : // static
    3389             : void
    3390           0 : CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
    3391             : {
    3392           0 :   LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
    3393             :        aClosure));
    3394             : 
    3395           0 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    3396             : 
    3397           0 :   if (!ioMan) {
    3398           0 :     return;
    3399             :   }
    3400             : 
    3401           0 :   ioMan->mTrashTimer = nullptr;
    3402           0 :   ioMan->StartRemovingTrash();
    3403             : }
    3404             : 
    3405             : nsresult
    3406           1 : CacheFileIOManager::StartRemovingTrash()
    3407             : {
    3408           1 :   LOG(("CacheFileIOManager::StartRemovingTrash()"));
    3409             : 
    3410             :   nsresult rv;
    3411             : 
    3412           1 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3413             : 
    3414           1 :   if (mShuttingDown) {
    3415           0 :     return NS_ERROR_NOT_INITIALIZED;
    3416             :   }
    3417             : 
    3418           1 :   if (!mCacheDirectory) {
    3419           0 :     return NS_ERROR_FILE_INVALID_PATH;
    3420             :   }
    3421             : 
    3422           1 :   if (mTrashTimer) {
    3423           0 :     LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
    3424           0 :     return NS_OK;
    3425             :   }
    3426             : 
    3427           1 :   if (mRemovingTrashDirs) {
    3428           0 :     LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
    3429             :          "progress."));
    3430           0 :     return NS_OK;
    3431             :   }
    3432             : 
    3433           1 :   uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
    3434           1 :   if (elapsed < kRemoveTrashStartDelay) {
    3435           2 :     nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    3436           1 :     NS_ENSURE_SUCCESS(rv, rv);
    3437             : 
    3438           2 :     nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
    3439           1 :     MOZ_ASSERT(ioTarget);
    3440             : 
    3441           1 :     rv = timer->SetTarget(ioTarget);
    3442           1 :     NS_ENSURE_SUCCESS(rv, rv);
    3443             : 
    3444           2 :     rv = timer->InitWithNamedFuncCallback(
    3445             :       CacheFileIOManager::OnTrashTimer,
    3446             :       nullptr,
    3447             :       kRemoveTrashStartDelay - elapsed,
    3448             :       nsITimer::TYPE_ONE_SHOT,
    3449           1 :       "net::CacheFileIOManager::StartRemovingTrash");
    3450           1 :     NS_ENSURE_SUCCESS(rv, rv);
    3451             : 
    3452           1 :     mTrashTimer.swap(timer);
    3453           1 :     return NS_OK;
    3454             :   }
    3455             : 
    3456           0 :   nsCOMPtr<nsIRunnable> ev;
    3457           0 :   ev = NewRunnableMethod("net::CacheFileIOManager::RemoveTrashInternal",
    3458             :                          this,
    3459           0 :                          &CacheFileIOManager::RemoveTrashInternal);
    3460             : 
    3461           0 :   rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
    3462           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3463             : 
    3464           0 :   mRemovingTrashDirs = true;
    3465           0 :   return NS_OK;
    3466             : }
    3467             : 
    3468             : nsresult
    3469           0 : CacheFileIOManager::RemoveTrashInternal()
    3470             : {
    3471           0 :   LOG(("CacheFileIOManager::RemoveTrashInternal()"));
    3472             : 
    3473             :   nsresult rv;
    3474             : 
    3475           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3476             : 
    3477           0 :   if (mShuttingDown) {
    3478           0 :     return NS_ERROR_NOT_INITIALIZED;
    3479             :   }
    3480             : 
    3481           0 :   CacheIOThread::Cancelable cancelable(true);
    3482             : 
    3483           0 :   MOZ_ASSERT(!mTrashTimer);
    3484           0 :   MOZ_ASSERT(mRemovingTrashDirs);
    3485             : 
    3486           0 :   if (!mTreeCreated) {
    3487           0 :     rv = CreateCacheTree();
    3488           0 :     if (NS_FAILED(rv)) {
    3489           0 :       return rv;
    3490             :     }
    3491             :   }
    3492             : 
    3493             :   // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
    3494             :   // here and set it again once we dispatch a continuation event. By doing so,
    3495             :   // we don't have to drop the flag on any possible early return.
    3496           0 :   mRemovingTrashDirs = false;
    3497             : 
    3498             :   while (true) {
    3499           0 :     if (CacheIOThread::YieldAndRerun()) {
    3500           0 :       LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
    3501             :            "higher level events."));
    3502           0 :       mRemovingTrashDirs = true;
    3503           0 :       return NS_OK;
    3504             :     }
    3505             : 
    3506             :     // Find some trash directory
    3507           0 :     if (!mTrashDir) {
    3508           0 :       MOZ_ASSERT(!mTrashDirEnumerator);
    3509             : 
    3510           0 :       rv = FindTrashDirToRemove();
    3511           0 :       if (rv == NS_ERROR_NOT_AVAILABLE) {
    3512           0 :         LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
    3513             :              "found."));
    3514           0 :         return NS_OK;
    3515             :       }
    3516           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3517             : 
    3518           0 :       nsCOMPtr<nsISimpleEnumerator> enumerator;
    3519           0 :       rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
    3520           0 :       if (NS_SUCCEEDED(rv)) {
    3521           0 :         mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
    3522           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3523             :       }
    3524             : 
    3525           0 :       continue; // check elapsed time
    3526             :     }
    3527             : 
    3528             :     // We null out mTrashDirEnumerator once we remove all files in the
    3529             :     // directory, so remove the trash directory if we don't have enumerator.
    3530           0 :     if (!mTrashDirEnumerator) {
    3531           0 :       rv = mTrashDir->Remove(false);
    3532           0 :       if (NS_FAILED(rv)) {
    3533             :         // There is no reason why removing an empty directory should fail, but
    3534             :         // if it does, we should continue and try to remove all other trash
    3535             :         // directories.
    3536           0 :         nsAutoCString leafName;
    3537           0 :         mTrashDir->GetNativeLeafName(leafName);
    3538           0 :         mFailedTrashDirs.AppendElement(leafName);
    3539           0 :         LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
    3540             :              "trashdir. [name=%s]", leafName.get()));
    3541             :       }
    3542             : 
    3543           0 :       mTrashDir = nullptr;
    3544           0 :       continue; // check elapsed time
    3545             :     }
    3546             : 
    3547           0 :     nsCOMPtr<nsIFile> file;
    3548           0 :     rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
    3549           0 :     if (!file) {
    3550           0 :       mTrashDirEnumerator->Close();
    3551           0 :       mTrashDirEnumerator = nullptr;
    3552           0 :       continue; // check elapsed time
    3553             :     } else {
    3554           0 :       bool isDir = false;
    3555           0 :       file->IsDirectory(&isDir);
    3556           0 :       if (isDir) {
    3557             :         NS_WARNING("Found a directory in a trash directory! It will be removed "
    3558           0 :                    "recursively, but this can block IO thread for a while!");
    3559           0 :         if (LOG_ENABLED()) {
    3560           0 :           nsAutoCString path;
    3561           0 :           file->GetNativePath(path);
    3562           0 :           LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
    3563             :               "directory! It will be removed recursively, but this can block IO "
    3564             :               "thread for a while! [file=%s]", path.get()));
    3565             :         }
    3566             :       }
    3567           0 :       file->Remove(isDir);
    3568             :     }
    3569           0 :   }
    3570             : 
    3571             :   NS_NOTREACHED("We should never get here");
    3572             :   return NS_OK;
    3573             : }
    3574             : 
    3575             : nsresult
    3576           0 : CacheFileIOManager::FindTrashDirToRemove()
    3577             : {
    3578           0 :   LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
    3579             : 
    3580             :   nsresult rv;
    3581             : 
    3582             :   // We call this method on the main thread during shutdown when user wants to
    3583             :   // remove all cache files.
    3584           0 :   MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
    3585             : 
    3586           0 :   nsCOMPtr<nsISimpleEnumerator> iter;
    3587           0 :   rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
    3588           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3589             : 
    3590             :   bool more;
    3591           0 :   nsCOMPtr<nsISupports> elem;
    3592             : 
    3593           0 :   while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
    3594           0 :     rv = iter->GetNext(getter_AddRefs(elem));
    3595           0 :     if (NS_FAILED(rv)) {
    3596           0 :       continue;
    3597             :     }
    3598             : 
    3599           0 :     nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
    3600           0 :     if (!file) {
    3601           0 :       continue;
    3602             :     }
    3603             : 
    3604           0 :     bool isDir = false;
    3605           0 :     file->IsDirectory(&isDir);
    3606           0 :     if (!isDir) {
    3607           0 :       continue;
    3608             :     }
    3609             : 
    3610           0 :     nsAutoCString leafName;
    3611           0 :     rv = file->GetNativeLeafName(leafName);
    3612           0 :     if (NS_FAILED(rv)) {
    3613           0 :       continue;
    3614             :     }
    3615             : 
    3616           0 :     if (leafName.Length() < strlen(TRASH_DIR)) {
    3617           0 :       continue;
    3618             :     }
    3619             : 
    3620           0 :     if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(TRASH_DIR))) {
    3621           0 :       continue;
    3622             :     }
    3623             : 
    3624           0 :     if (mFailedTrashDirs.Contains(leafName)) {
    3625           0 :       continue;
    3626             :     }
    3627             : 
    3628           0 :     LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
    3629             :          leafName.get()));
    3630             : 
    3631           0 :     mTrashDir = file;
    3632           0 :     return NS_OK;
    3633             :   }
    3634             : 
    3635             :   // When we're here we've tried to delete all trash directories. Clear
    3636             :   // mFailedTrashDirs so we will try to delete them again when we start removing
    3637             :   // trash directories next time.
    3638           0 :   mFailedTrashDirs.Clear();
    3639           0 :   return NS_ERROR_NOT_AVAILABLE;
    3640             : }
    3641             : 
    3642             : // static
    3643             : nsresult
    3644           5 : CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
    3645             :                                    OriginAttrsHash  aOriginAttrsHash,
    3646             :                                    bool             aAnonymous,
    3647             :                                    bool             aPinning)
    3648             : {
    3649           5 :   LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, originAttrsHash=%" PRIx64 ", "
    3650             :        "anonymous=%d, pinning=%d]", aHandle, aOriginAttrsHash, aAnonymous,
    3651             :        aPinning));
    3652             : 
    3653             :   nsresult rv;
    3654          10 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    3655             : 
    3656           5 :   if (aHandle->IsClosed() || !ioMan) {
    3657           0 :     return NS_ERROR_NOT_INITIALIZED;
    3658             :   }
    3659             : 
    3660           5 :   if (aHandle->IsSpecialFile()) {
    3661           0 :     return NS_ERROR_UNEXPECTED;
    3662             :   }
    3663             : 
    3664             :   RefPtr<InitIndexEntryEvent> ev =
    3665          10 :     new InitIndexEntryEvent(aHandle, aOriginAttrsHash, aAnonymous, aPinning);
    3666          10 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    3667             :                                   ? CacheIOThread::WRITE_PRIORITY
    3668          10 :                                   : CacheIOThread::WRITE);
    3669           5 :   NS_ENSURE_SUCCESS(rv, rv);
    3670             : 
    3671           5 :   return NS_OK;
    3672             : }
    3673             : 
    3674             : // static
    3675             : nsresult
    3676          14 : CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
    3677             :                                      const uint32_t  *aFrecency,
    3678             :                                      const uint32_t  *aExpirationTime,
    3679             :                                      const bool      *aHasAltData,
    3680             :                                      const uint16_t  *aOnStartTime,
    3681             :                                      const uint16_t  *aOnStopTime)
    3682             : {
    3683          14 :   LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
    3684             :        "expirationTime=%s, hasAltData=%s, onStartTime=%s, onStopTime=%s]", aHandle,
    3685             :        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
    3686             :        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
    3687             :        aHasAltData ? (*aHasAltData ? "true" : "false") : "",
    3688             :        aOnStartTime ? nsPrintfCString("%u", *aOnStartTime).get() : "",
    3689             :        aOnStopTime ? nsPrintfCString("%u", *aOnStopTime).get() : ""));
    3690             : 
    3691             :   nsresult rv;
    3692          28 :   RefPtr<CacheFileIOManager> ioMan = gInstance;
    3693             : 
    3694          14 :   if (aHandle->IsClosed() || !ioMan) {
    3695           0 :     return NS_ERROR_NOT_INITIALIZED;
    3696             :   }
    3697             : 
    3698          14 :   if (aHandle->IsSpecialFile()) {
    3699           0 :     return NS_ERROR_UNEXPECTED;
    3700             :   }
    3701             : 
    3702             :   RefPtr<UpdateIndexEntryEvent> ev =
    3703             :     new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime, aHasAltData,
    3704          28 :                               aOnStartTime, aOnStopTime);
    3705          28 :   rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
    3706             :                                   ? CacheIOThread::WRITE_PRIORITY
    3707          28 :                                   : CacheIOThread::WRITE);
    3708          14 :   NS_ENSURE_SUCCESS(rv, rv);
    3709             : 
    3710          14 :   return NS_OK;
    3711             : }
    3712             : 
    3713             : nsresult
    3714           2 : CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
    3715             : {
    3716           2 :   MOZ_ASSERT(!aHandle->mFD);
    3717           2 :   MOZ_ASSERT(aHandle->mFile);
    3718             : 
    3719             :   nsresult rv;
    3720             : 
    3721           2 :   if (aHandle->IsDoomed()) {
    3722           0 :     nsCOMPtr<nsIFile> file;
    3723             : 
    3724           0 :     rv = GetDoomedFile(getter_AddRefs(file));
    3725           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3726             : 
    3727           0 :     aHandle->mFile.swap(file);
    3728             :   } else {
    3729             :     bool exists;
    3730           2 :     if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
    3731           0 :       NS_WARNING("Found a file that should not exist!");
    3732             :     }
    3733             :   }
    3734             : 
    3735           2 :   rv = OpenNSPRHandle(aHandle, true);
    3736           2 :   NS_ENSURE_SUCCESS(rv, rv);
    3737             : 
    3738           2 :   aHandle->mFileSize = 0;
    3739           2 :   return NS_OK;
    3740             : }
    3741             : 
    3742             : // static
    3743             : void
    3744           6 : CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
    3745             : {
    3746           6 :   _retval.Truncate();
    3747             :   const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
    3748           6 :                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    3749         126 :   for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
    3750         120 :     _retval.Append(hexChars[(*aHash)[i] >> 4]);
    3751         120 :     _retval.Append(hexChars[(*aHash)[i] & 0xF]);
    3752             :   }
    3753           6 : }
    3754             : 
    3755             : // static
    3756             : nsresult
    3757           0 : CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
    3758             : {
    3759           0 :   if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
    3760           0 :     return NS_ERROR_INVALID_ARG;
    3761             :   }
    3762             : 
    3763           0 :   for (uint32_t i=0 ; i<aHash.Length() ; i++) {
    3764             :     uint8_t value;
    3765             : 
    3766           0 :     if (aHash[i] >= '0' && aHash[i] <= '9') {
    3767           0 :       value = aHash[i] - '0';
    3768           0 :     } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
    3769           0 :       value = aHash[i] - 'A' + 10;
    3770           0 :     } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
    3771           0 :       value = aHash[i] - 'a' + 10;
    3772             :     } else {
    3773           0 :       return NS_ERROR_INVALID_ARG;
    3774             :     }
    3775             : 
    3776           0 :     if (i%2 == 0) {
    3777           0 :       (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
    3778             :     } else {
    3779           0 :       (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
    3780             :     }
    3781             :   }
    3782             : 
    3783           0 :   return NS_OK;
    3784             : }
    3785             : 
    3786             : nsresult
    3787           6 : CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
    3788             : {
    3789             :   nsresult rv;
    3790          12 :   nsCOMPtr<nsIFile> file;
    3791           6 :   rv = mCacheDirectory->Clone(getter_AddRefs(file));
    3792           6 :   NS_ENSURE_SUCCESS(rv, rv);
    3793             : 
    3794           6 :   rv = file->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR));
    3795           6 :   NS_ENSURE_SUCCESS(rv, rv);
    3796             : 
    3797          12 :   nsAutoCString leafName;
    3798           6 :   HashToStr(aHash, leafName);
    3799             : 
    3800           6 :   rv = file->AppendNative(leafName);
    3801           6 :   NS_ENSURE_SUCCESS(rv, rv);
    3802             : 
    3803           6 :   file.swap(*_retval);
    3804           6 :   return NS_OK;
    3805             : }
    3806             : 
    3807             : nsresult
    3808           3 : CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
    3809             : {
    3810             :   nsresult rv;
    3811           6 :   nsCOMPtr<nsIFile> file;
    3812           3 :   rv = mCacheDirectory->Clone(getter_AddRefs(file));
    3813           3 :   NS_ENSURE_SUCCESS(rv, rv);
    3814             : 
    3815           3 :   rv = file->AppendNative(aKey);
    3816           3 :   NS_ENSURE_SUCCESS(rv, rv);
    3817             : 
    3818           3 :   file.swap(*_retval);
    3819           3 :   return NS_OK;
    3820             : }
    3821             : 
    3822             : nsresult
    3823           0 : CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
    3824             : {
    3825             :   nsresult rv;
    3826           0 :   nsCOMPtr<nsIFile> file;
    3827           0 :   rv = mCacheDirectory->Clone(getter_AddRefs(file));
    3828           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3829             : 
    3830           0 :   rv = file->AppendNative(NS_LITERAL_CSTRING(DOOMED_DIR));
    3831           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3832             : 
    3833           0 :   rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
    3834           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3835             : 
    3836           0 :   const int32_t kMaxTries = 64;
    3837           0 :   srand(static_cast<unsigned>(PR_Now()));
    3838           0 :   nsAutoCString leafName;
    3839           0 :   for (int32_t triesCount = 0; ; ++triesCount) {
    3840           0 :     leafName.AppendInt(rand());
    3841           0 :     rv = file->SetNativeLeafName(leafName);
    3842           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3843             : 
    3844             :     bool exists;
    3845           0 :     if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
    3846           0 :       break;
    3847             :     }
    3848             : 
    3849           0 :     if (triesCount == kMaxTries) {
    3850           0 :       LOG(("CacheFileIOManager::GetDoomedFile() - Could not find unused file "
    3851             :            "name in %d tries.", kMaxTries));
    3852           0 :       return NS_ERROR_FAILURE;
    3853             :     }
    3854             : 
    3855           0 :     leafName.Truncate();
    3856           0 :   }
    3857             : 
    3858           0 :   file.swap(*_retval);
    3859           0 :   return NS_OK;
    3860             : }
    3861             : 
    3862             : nsresult
    3863           1 : CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
    3864             : {
    3865           1 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3866             : 
    3867             :   nsresult rv;
    3868             : 
    3869           2 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
    3870           1 :   rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
    3871           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3872             : 
    3873           1 :   bool hasMoreElements = false;
    3874           1 :   rv = enumerator->HasMoreElements(&hasMoreElements);
    3875           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3876             : 
    3877           1 :   *_retval = !hasMoreElements;
    3878           1 :   return NS_OK;
    3879             : }
    3880             : 
    3881             : nsresult
    3882           4 : CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
    3883             :                                       bool aEnsureEmptyDir)
    3884             : {
    3885             :   nsresult rv;
    3886             : 
    3887           8 :   nsCOMPtr<nsIFile> file;
    3888           4 :   if (!aDir) {
    3889           2 :     file = aFile;
    3890             :   } else {
    3891           4 :     nsAutoCString dir(aDir);
    3892           2 :     rv = aFile->Clone(getter_AddRefs(file));
    3893           2 :     NS_ENSURE_SUCCESS(rv, rv);
    3894           2 :     rv = file->AppendNative(dir);
    3895           2 :     NS_ENSURE_SUCCESS(rv, rv);
    3896             :   }
    3897             : 
    3898           4 :   bool exists = false;
    3899           4 :   rv = file->Exists(&exists);
    3900           4 :   if (NS_SUCCEEDED(rv) && exists) {
    3901           4 :     bool isDirectory = false;
    3902           4 :     rv = file->IsDirectory(&isDirectory);
    3903           4 :     if (NS_FAILED(rv) || !isDirectory) {
    3904             :       // Try to remove the file
    3905           0 :       rv = file->Remove(false);
    3906           0 :       if (NS_SUCCEEDED(rv)) {
    3907           0 :         exists = false;
    3908             :       }
    3909             :     }
    3910           4 :     NS_ENSURE_SUCCESS(rv, rv);
    3911             :   }
    3912             : 
    3913           4 :   if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
    3914             :     bool isEmpty;
    3915           1 :     rv = IsEmptyDirectory(file, &isEmpty);
    3916           1 :     NS_ENSURE_SUCCESS(rv, rv);
    3917             : 
    3918           1 :     if (!isEmpty) {
    3919             :       // Don't check the result, if this fails, it's OK.  We do this
    3920             :       // only for the doomed directory that doesn't need to be deleted
    3921             :       // for the cost of completely disabling the whole browser.
    3922           0 :       TrashDirectory(file);
    3923             :     }
    3924             :   }
    3925             : 
    3926           4 :   if (NS_SUCCEEDED(rv) && !exists) {
    3927           0 :     rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
    3928             :   }
    3929           4 :   if (NS_FAILED(rv)) {
    3930           0 :     NS_WARNING("Cannot create directory");
    3931           0 :     return NS_ERROR_FAILURE;
    3932             :   }
    3933             : 
    3934           4 :   return NS_OK;
    3935             : }
    3936             : 
    3937             : nsresult
    3938           1 : CacheFileIOManager::CreateCacheTree()
    3939             : {
    3940           1 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    3941           1 :   MOZ_ASSERT(!mTreeCreated);
    3942             : 
    3943           1 :   if (!mCacheDirectory || mTreeCreationFailed) {
    3944           0 :     return NS_ERROR_FILE_INVALID_PATH;
    3945             :   }
    3946             : 
    3947             :   nsresult rv;
    3948             : 
    3949             :   // Set the flag here and clear it again below when the tree is created
    3950             :   // successfully.
    3951           1 :   mTreeCreationFailed = true;
    3952             : 
    3953             :   // ensure parent directory exists
    3954           2 :   nsCOMPtr<nsIFile> parentDir;
    3955           1 :   rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
    3956           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3957           1 :   rv = CheckAndCreateDir(parentDir, nullptr, false);
    3958           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3959             : 
    3960             :   // ensure cache directory exists
    3961           1 :   rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
    3962           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3963             : 
    3964             :   // ensure entries directory exists
    3965           1 :   rv = CheckAndCreateDir(mCacheDirectory, ENTRIES_DIR, false);
    3966           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3967             : 
    3968             :   // ensure doomed directory exists
    3969           1 :   rv = CheckAndCreateDir(mCacheDirectory, DOOMED_DIR, true);
    3970           1 :   NS_ENSURE_SUCCESS(rv, rv);
    3971             : 
    3972           1 :   mTreeCreated = true;
    3973           1 :   mTreeCreationFailed = false;
    3974             : 
    3975           1 :   if (!mContextEvictor) {
    3976           2 :     RefPtr<CacheFileContextEvictor> contextEvictor;
    3977           1 :     contextEvictor = new CacheFileContextEvictor();
    3978             : 
    3979             :     // Init() method will try to load unfinished contexts from the disk. Store
    3980             :     // the evictor as a member only when there is some unfinished job.
    3981           1 :     contextEvictor->Init(mCacheDirectory);
    3982           1 :     if (contextEvictor->ContextsCount()) {
    3983           0 :       contextEvictor.swap(mContextEvictor);
    3984             :     }
    3985             :   }
    3986             : 
    3987           1 :   StartRemovingTrash();
    3988             : 
    3989           1 :   if (!CacheObserver::CacheFSReported()) {
    3990           1 :     uint32_t fsType = 4; // Other OS
    3991             : 
    3992             : #ifdef XP_WIN
    3993             :     nsAutoString target;
    3994             :     nsresult rv = mCacheDirectory->GetTarget(target);
    3995             :     if (NS_FAILED(rv)) {
    3996             :       return NS_OK;
    3997             :     }
    3998             : 
    3999             :     wchar_t volume_path[MAX_PATH + 1] = { 0 };
    4000             :     if (!::GetVolumePathNameW(target.get(),
    4001             :                               volume_path,
    4002             :                               mozilla::ArrayLength(volume_path))) {
    4003             :       return NS_OK;
    4004             :     }
    4005             : 
    4006             :     wchar_t fsName[6] = { 0 };
    4007             :     if (!::GetVolumeInformationW(volume_path, nullptr, 0, nullptr, nullptr,
    4008             :                                  nullptr, fsName,
    4009             :                                  mozilla::ArrayLength(fsName))) {
    4010             :       return NS_OK;
    4011             :     }
    4012             : 
    4013             :     if (wcscmp(fsName, L"NTFS") == 0) {
    4014             :       fsType = 0;
    4015             :     } else if (wcscmp(fsName, L"FAT32") == 0) {
    4016             :       fsType = 1;
    4017             :     } else if (wcscmp(fsName, L"FAT") == 0) {
    4018             :       fsType = 2;
    4019             :     } else {
    4020             :       fsType = 3;
    4021             :     }
    4022             : #endif
    4023             : 
    4024           1 :     Telemetry::Accumulate(Telemetry::NETWORK_CACHE_FS_TYPE, fsType);
    4025           1 :     CacheObserver::SetCacheFSReported();
    4026             :   }
    4027             : 
    4028           1 :   return NS_OK;
    4029             : }
    4030             : 
    4031             : nsresult
    4032           5 : CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
    4033             : {
    4034           5 :   LOG(("CacheFileIOManager::OpenNSPRHandle BEGIN, handle=%p", aHandle));
    4035             : 
    4036           5 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    4037           5 :   MOZ_ASSERT(!aHandle->mFD);
    4038           5 :   MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
    4039           5 :   MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
    4040           5 :   MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
    4041             :              (!aCreate && aHandle->mFileExists));
    4042             : 
    4043             :   nsresult rv;
    4044             : 
    4045           5 :   if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
    4046             :     // close handle that hasn't been used for the longest time
    4047           0 :     rv = MaybeReleaseNSPRHandleInternal(mHandlesByLastUsed[0], true);
    4048           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4049             :   }
    4050             : 
    4051           5 :   if (aCreate) {
    4052           4 :     rv = aHandle->mFile->OpenNSPRFileDesc(
    4053           4 :            PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
    4054           2 :     if (rv == NS_ERROR_FILE_ALREADY_EXISTS ||  // error from nsLocalFileWin
    4055             :         rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { // error from nsLocalFileUnix
    4056           0 :       LOG(("CacheFileIOManager::OpenNSPRHandle() - Cannot create a new file, we"
    4057             :            " might reached a limit on FAT32. Will evict a single entry and try "
    4058             :            "again. [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHandle->Hash())));
    4059             : 
    4060             :       SHA1Sum::Hash hash;
    4061             :       uint32_t cnt;
    4062             : 
    4063           0 :       rv = CacheIndex::GetEntryForEviction(true, &hash, &cnt);
    4064           0 :       if (NS_SUCCEEDED(rv)) {
    4065           0 :         rv = DoomFileByKeyInternal(&hash);
    4066             :       }
    4067           0 :       if (NS_SUCCEEDED(rv)) {
    4068           0 :         rv = aHandle->mFile->OpenNSPRFileDesc(
    4069           0 :                PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
    4070           0 :         LOG(("CacheFileIOManager::OpenNSPRHandle() - Successfully evicted entry"
    4071             :              " with hash %08x%08x%08x%08x%08x. %s to create the new file.",
    4072             :              LOGSHA1(&hash), NS_SUCCEEDED(rv) ? "Succeeded" : "Failed"));
    4073             : 
    4074             :         // Report the full size only once per session
    4075             :         static bool sSizeReported = false;
    4076           0 :         if (!sSizeReported) {
    4077             :           uint32_t cacheUsage;
    4078           0 :           if (NS_SUCCEEDED(CacheIndex::GetCacheSize(&cacheUsage))) {
    4079           0 :             cacheUsage >>= 10;
    4080             :             Telemetry::Accumulate(Telemetry::NETWORK_CACHE_SIZE_FULL_FAT,
    4081           0 :                                   cacheUsage);
    4082           0 :             sSizeReported = true;
    4083             :           }
    4084             :         }
    4085             :       } else {
    4086           0 :         LOG(("CacheFileIOManager::OpenNSPRHandle() - Couldn't evict an existing"
    4087             :              " entry."));
    4088           0 :         rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
    4089             :       }
    4090             :     }
    4091           2 :     if (NS_FAILED(rv)) {
    4092           0 :       LOG(("CacheFileIOManager::OpenNSPRHandle() Create failed with 0x%08" PRIx32,
    4093             :            static_cast<uint32_t>(rv)));
    4094             :     }
    4095           2 :     NS_ENSURE_SUCCESS(rv, rv);
    4096             : 
    4097           2 :     aHandle->mFileExists = true;
    4098             :   } else {
    4099           3 :     rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
    4100           3 :     if (NS_ERROR_FILE_NOT_FOUND == rv) {
    4101           0 :       LOG(("  file doesn't exists"));
    4102           0 :       aHandle->mFileExists = false;
    4103           0 :       return DoomFileInternal(aHandle);
    4104             :     }
    4105           3 :     if (NS_FAILED(rv)) {
    4106           0 :       LOG(("CacheFileIOManager::OpenNSPRHandle() Open failed with 0x%08" PRIx32,
    4107             :            static_cast<uint32_t>(rv)));
    4108             :     }
    4109           3 :     NS_ENSURE_SUCCESS(rv, rv);
    4110             :   }
    4111             : 
    4112           5 :   mHandlesByLastUsed.AppendElement(aHandle);
    4113             : 
    4114           5 :   LOG(("CacheFileIOManager::OpenNSPRHandle END, handle=%p", aHandle));
    4115             : 
    4116           5 :   return NS_OK;
    4117             : }
    4118             : 
    4119             : void
    4120           8 : CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
    4121             : {
    4122           8 :   MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
    4123           8 :   MOZ_ASSERT(aHandle->mFD);
    4124             : 
    4125          16 :   DebugOnly<bool> found;
    4126           8 :   found = mHandlesByLastUsed.RemoveElement(aHandle);
    4127           8 :   MOZ_ASSERT(found);
    4128             : 
    4129           8 :   mHandlesByLastUsed.AppendElement(aHandle);
    4130           8 : }
    4131             : 
    4132             : nsresult
    4133           0 : CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
    4134             : {
    4135             :   nsresult rv;
    4136           0 :   nsCOMPtr<nsIFile> file;
    4137             : 
    4138           0 :   if (!aDir) {
    4139           0 :     file = aFile;
    4140             :   } else {
    4141           0 :     rv = aFile->Clone(getter_AddRefs(file));
    4142           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4143           0 :       return rv;
    4144             :     }
    4145             : 
    4146           0 :     rv = file->AppendNative(nsDependentCString(aDir));
    4147           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4148           0 :       return rv;
    4149             :     }
    4150             :   }
    4151             : 
    4152           0 :   if (LOG_ENABLED()) {
    4153           0 :     nsAutoCString path;
    4154           0 :     file->GetNativePath(path);
    4155           0 :     LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
    4156             :          path.get()));
    4157             :   }
    4158             : 
    4159           0 :   rv = file->Remove(true);
    4160           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4161           0 :     LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08" PRIx32 "]",
    4162             :          static_cast<uint32_t>(rv)));
    4163             :   }
    4164             : 
    4165           0 :   return rv;
    4166             : }
    4167             : 
    4168             : void
    4169           0 : CacheFileIOManager::SyncRemoveAllCacheFiles()
    4170             : {
    4171           0 :   LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
    4172             : 
    4173             :   nsresult rv;
    4174             : 
    4175           0 :   SyncRemoveDir(mCacheDirectory, ENTRIES_DIR);
    4176           0 :   SyncRemoveDir(mCacheDirectory, DOOMED_DIR);
    4177             : 
    4178             :   // Clear any intermediate state of trash dir enumeration.
    4179           0 :   mFailedTrashDirs.Clear();
    4180           0 :   mTrashDir = nullptr;
    4181             : 
    4182             :   while (true) {
    4183             :     // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
    4184           0 :     rv = FindTrashDirToRemove();
    4185           0 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
    4186           0 :       LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
    4187             :            "found."));
    4188           0 :       break;
    4189             :     }
    4190           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    4191           0 :       LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
    4192             :            "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08" PRIx32 "]",
    4193             :            static_cast<uint32_t>(rv)));
    4194           0 :       break;
    4195             :     }
    4196             : 
    4197           0 :     rv = SyncRemoveDir(mTrashDir, nullptr);
    4198           0 :     if (NS_FAILED(rv)) {
    4199           0 :       nsAutoCString leafName;
    4200           0 :       mTrashDir->GetNativeLeafName(leafName);
    4201           0 :       mFailedTrashDirs.AppendElement(leafName);
    4202             :     }
    4203           0 :   }
    4204           0 : }
    4205             : 
    4206             : // Returns default ("smart") size (in KB) of cache, given available disk space
    4207             : // (also in KB)
    4208             : static uint32_t
    4209           0 : SmartCacheSize(const uint32_t availKB)
    4210             : {
    4211           0 :   uint32_t maxSize = kMaxCacheSizeKB;
    4212             : 
    4213           0 :   if (availKB > 100 * 1024 * 1024) {
    4214           0 :     return maxSize;  // skip computing if we're over 100 GB
    4215             :   }
    4216             : 
    4217             :   // Grow/shrink in 10 MB units, deliberately, so that in the common case we
    4218             :   // don't shrink cache and evict items every time we startup (it's important
    4219             :   // that we don't slow down startup benchmarks).
    4220           0 :   uint32_t sz10MBs = 0;
    4221           0 :   uint32_t avail10MBs = availKB / (1024*10);
    4222             : 
    4223             :   // .5% of space above 25 GB
    4224           0 :   if (avail10MBs > 2500) {
    4225           0 :     sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
    4226           0 :     avail10MBs = 2500;
    4227             :   }
    4228             :   // 1% of space between 7GB -> 25 GB
    4229           0 :   if (avail10MBs > 700) {
    4230           0 :     sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
    4231           0 :     avail10MBs = 700;
    4232             :   }
    4233             :   // 5% of space between 500 MB -> 7 GB
    4234           0 :   if (avail10MBs > 50) {
    4235           0 :     sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
    4236           0 :     avail10MBs = 50;
    4237             :   }
    4238             : 
    4239             : #ifdef ANDROID
    4240             :   // On Android, smaller/older devices may have very little storage and
    4241             :   // device owners may be sensitive to storage footprint: Use a smaller
    4242             :   // percentage of available space and a smaller minimum.
    4243             : 
    4244             :   // 20% of space up to 500 MB (10 MB min)
    4245             :   sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
    4246             : #else
    4247             :   // 40% of space up to 500 MB (50 MB min)
    4248           0 :   sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
    4249             : #endif
    4250             : 
    4251           0 :   return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
    4252             : }
    4253             : 
    4254             : nsresult
    4255           4 : CacheFileIOManager::UpdateSmartCacheSize(int64_t aFreeSpace)
    4256             : {
    4257           4 :   MOZ_ASSERT(mIOThread->IsCurrentThread());
    4258             : 
    4259             :   nsresult rv;
    4260             : 
    4261           4 :   if (!CacheObserver::UseNewCache()) {
    4262           0 :     return NS_ERROR_NOT_AVAILABLE;
    4263             :   }
    4264             : 
    4265           4 :   if (!CacheObserver::SmartCacheSizeEnabled()) {
    4266           4 :     return NS_ERROR_NOT_AVAILABLE;
    4267             :   }
    4268             : 
    4269             :   // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
    4270             :   static const TimeDuration kUpdateLimit =
    4271           0 :     TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
    4272           0 :   if (!mLastSmartSizeTime.IsNull() &&
    4273           0 :       (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
    4274           0 :     return NS_OK;
    4275             :   }
    4276             : 
    4277             :   // Do not compute smart size when cache size is not reliable.
    4278           0 :   bool isUpToDate = false;
    4279           0 :   CacheIndex::IsUpToDate(&isUpToDate);
    4280           0 :   if (!isUpToDate) {
    4281           0 :     return NS_ERROR_NOT_AVAILABLE;
    4282             :   }
    4283             : 
    4284             :   uint32_t cacheUsage;
    4285           0 :   rv = CacheIndex::GetCacheSize(&cacheUsage);
    4286           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4287           0 :     LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
    4288             :          "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
    4289           0 :     return rv;
    4290             :   }
    4291             : 
    4292           0 :   mLastSmartSizeTime = TimeStamp::NowLoRes();
    4293             : 
    4294           0 :   uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(aFreeSpace / 1024) +
    4295           0 :                                       cacheUsage);
    4296             : 
    4297           0 :   if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
    4298             :     // Smart size has not changed.
    4299           0 :     return NS_OK;
    4300             :   }
    4301             : 
    4302           0 :   CacheObserver::SetDiskCacheCapacity(smartSize << 10);
    4303             : 
    4304           0 :   return NS_OK;
    4305             : }
    4306             : 
    4307             : // Memory reporting
    4308             : 
    4309             : namespace {
    4310             : 
    4311             : // A helper class that dispatches and waits for an event that gets result of
    4312             : // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
    4313             : // to safely get handles memory report.
    4314             : // We must do this, since the handle list is only accessed and managed w/o
    4315             : // locking on the I/O thread.  That is by design.
    4316           0 : class SizeOfHandlesRunnable : public Runnable
    4317             : {
    4318             : public:
    4319           0 :   SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
    4320             :                         CacheFileHandles const& handles,
    4321             :                         nsTArray<CacheFileHandle*> const& specialHandles)
    4322           0 :     : Runnable("net::SizeOfHandlesRunnable")
    4323             :     , mMonitor("SizeOfHandlesRunnable.mMonitor")
    4324             :     , mMallocSizeOf(mallocSizeOf)
    4325             :     , mHandles(handles)
    4326           0 :     , mSpecialHandles(specialHandles)
    4327             :   {
    4328           0 :   }
    4329             : 
    4330           0 :   size_t Get(CacheIOThread* thread)
    4331             :   {
    4332           0 :     nsCOMPtr<nsIEventTarget> target = thread->Target();
    4333           0 :     if (!target) {
    4334           0 :       NS_ERROR("If we have the I/O thread we also must have the I/O target");
    4335           0 :       return 0;
    4336             :     }
    4337             : 
    4338           0 :     mozilla::MonitorAutoLock mon(mMonitor);
    4339           0 :     mMonitorNotified = false;
    4340           0 :     nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
    4341           0 :     if (NS_FAILED(rv)) {
    4342           0 :       NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
    4343           0 :       return 0;
    4344             :     }
    4345             : 
    4346           0 :     while (!mMonitorNotified) {
    4347           0 :       mon.Wait();
    4348             :     }
    4349           0 :     return mSize;
    4350             :   }
    4351             : 
    4352           0 :   NS_IMETHOD Run() override
    4353             :   {
    4354           0 :     mozilla::MonitorAutoLock mon(mMonitor);
    4355             :     // Excluding this since the object itself is a member of CacheFileIOManager
    4356             :     // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
    4357           0 :     mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
    4358           0 :     for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
    4359           0 :       mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
    4360             :     }
    4361             : 
    4362           0 :     mMonitorNotified = true;
    4363           0 :     mon.Notify();
    4364           0 :     return NS_OK;
    4365             :   }
    4366             : 
    4367             : private:
    4368             :   mozilla::Monitor mMonitor;
    4369             :   bool mMonitorNotified;
    4370             :   mozilla::MallocSizeOf mMallocSizeOf;
    4371             :   CacheFileHandles const &mHandles;
    4372             :   nsTArray<CacheFileHandle *> const &mSpecialHandles;
    4373             :   size_t mSize;
    4374             : };
    4375             : 
    4376             : } // namespace
    4377             : 
    4378             : size_t
    4379           0 : CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
    4380             : {
    4381           0 :   size_t n = 0;
    4382           0 :   nsCOMPtr<nsISizeOf> sizeOf;
    4383             : 
    4384           0 :   if (mIOThread) {
    4385           0 :     n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
    4386             : 
    4387             :     // mHandles and mSpecialHandles must be accessed only on the I/O thread,
    4388             :     // must sync dispatch.
    4389             :     RefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
    4390           0 :       new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
    4391           0 :     n += sizeOfHandlesRunnable->Get(mIOThread);
    4392             :   }
    4393             : 
    4394             :   // mHandlesByLastUsed just refers handles reported by mHandles.
    4395             : 
    4396           0 :   sizeOf = do_QueryInterface(mCacheDirectory);
    4397           0 :   if (sizeOf)
    4398           0 :     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
    4399             : 
    4400           0 :   sizeOf = do_QueryInterface(mMetadataWritesTimer);
    4401           0 :   if (sizeOf)
    4402           0 :     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
    4403             : 
    4404           0 :   sizeOf = do_QueryInterface(mTrashTimer);
    4405           0 :   if (sizeOf)
    4406           0 :     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
    4407             : 
    4408           0 :   sizeOf = do_QueryInterface(mTrashDir);
    4409           0 :   if (sizeOf)
    4410           0 :     n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
    4411             : 
    4412           0 :   for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
    4413           0 :     n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
    4414             :   }
    4415             : 
    4416           0 :   return n;
    4417             : }
    4418             : 
    4419             : // static
    4420             : size_t
    4421           0 : CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
    4422             : {
    4423           0 :   if (!gInstance)
    4424           0 :     return 0;
    4425             : 
    4426           0 :   return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
    4427             : }
    4428             : 
    4429             : // static
    4430             : size_t
    4431           0 : CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
    4432             : {
    4433           0 :   return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
    4434             : }
    4435             : 
    4436             : } // namespace net
    4437             : } // namespace mozilla

Generated by: LCOV version 1.13