LCOV - code coverage report
Current view: directory - netwerk/cache - nsDiskCacheDevice.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 451 295 65.4 %
Date: 2012-06-02 Functions: 56 45 80.4 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is nsDiskCacheDevice.cpp, released
      17                 :  * February 22, 2001.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Gordon Sheridan <gordon@netscape.com>
      26                 :  *   Patrick C. Beard <beard@netscape.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include <limits.h>
      43                 : 
      44                 : // include files for ftruncate (or equivalent)
      45                 : #if defined(XP_UNIX)
      46                 : #include <unistd.h>
      47                 : #elif defined(XP_WIN)
      48                 : #include <windows.h>
      49                 : #elif defined(XP_OS2)
      50                 : #define INCL_DOSERRORS
      51                 : #include <os2.h>
      52                 : #else
      53                 : // XXX add necessary include file for ftruncate (or equivalent)
      54                 : #endif
      55                 : 
      56                 : #include "prtypes.h"
      57                 : #include "prthread.h"
      58                 : #include "prbit.h"
      59                 : 
      60                 : #include "private/pprio.h"
      61                 : 
      62                 : #include "nsDiskCacheDevice.h"
      63                 : #include "nsDiskCacheEntry.h"
      64                 : #include "nsDiskCacheMap.h"
      65                 : #include "nsDiskCacheStreams.h"
      66                 : 
      67                 : #include "nsDiskCache.h"
      68                 : 
      69                 : #include "nsCacheService.h"
      70                 : #include "nsCache.h"
      71                 : 
      72                 : #include "nsDeleteDir.h"
      73                 : 
      74                 : #include "nsICacheVisitor.h"
      75                 : #include "nsReadableUtils.h"
      76                 : #include "nsIInputStream.h"
      77                 : #include "nsIOutputStream.h"
      78                 : #include "nsCRT.h"
      79                 : #include "nsCOMArray.h"
      80                 : #include "nsISimpleEnumerator.h"
      81                 : 
      82                 : #include "mozilla/FunctionTimer.h"
      83                 : #include "nsThreadUtils.h"
      84                 : #include "mozilla/Telemetry.h"
      85                 : 
      86                 : static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
      87                 : using namespace mozilla;
      88                 : 
      89            3320 : class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable {
      90                 : public:
      91             830 :     nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device,
      92                 :                                           nsCacheEntry * entry,
      93                 :                                           nsDiskCacheBinding * binding)
      94                 :         : mCanceled(false),
      95                 :           mEntry(entry),
      96                 :           mDevice(device),
      97             830 :           mBinding(binding)
      98                 :     {
      99             830 :     }
     100                 : 
     101             830 :     NS_IMETHOD Run()
     102                 :     {
     103            1660 :         nsCacheServiceAutoLock lock;
     104                 : #ifdef PR_LOGGING
     105             830 :         CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this));
     106                 : #endif
     107             830 :         if (!mCanceled) {
     108             830 :             (void) mDevice->DeactivateEntry_Private(mEntry, mBinding);
     109                 :         }
     110             830 :         return NS_OK;
     111                 :     }
     112                 : 
     113               0 :     void CancelEvent() { mCanceled = true; }
     114                 : private:
     115                 :     bool mCanceled;
     116                 :     nsCacheEntry *mEntry;
     117                 :     nsDiskCacheDevice *mDevice;
     118                 :     nsDiskCacheBinding *mBinding;
     119                 : };
     120                 : 
     121               4 : class nsEvictDiskCacheEntriesEvent : public nsRunnable {
     122                 : public:
     123               1 :     nsEvictDiskCacheEntriesEvent(nsDiskCacheDevice *device)
     124               1 :         : mDevice(device) {}
     125                 : 
     126               1 :     NS_IMETHOD Run()
     127                 :     {
     128               2 :         nsCacheServiceAutoLock lock;
     129               1 :         mDevice->EvictDiskCacheEntries(mDevice->mCacheCapacity);
     130               1 :         return NS_OK;
     131                 :     }
     132                 : 
     133                 : private:
     134                 :     nsDiskCacheDevice *mDevice;
     135                 : };
     136                 : 
     137                 : /******************************************************************************
     138                 :  *  nsDiskCacheEvictor
     139                 :  *
     140                 :  *  Helper class for nsDiskCacheDevice.
     141                 :  *
     142                 :  *****************************************************************************/
     143                 : 
     144                 : class nsDiskCacheEvictor : public nsDiskCacheRecordVisitor
     145                 : {
     146                 : public:
     147              17 :     nsDiskCacheEvictor( nsDiskCacheMap *      cacheMap,
     148                 :                         nsDiskCacheBindery *  cacheBindery,
     149                 :                         PRUint32              targetSize,
     150                 :                         const char *          clientID)
     151                 :         : mCacheMap(cacheMap)
     152                 :         , mBindery(cacheBindery)
     153                 :         , mTargetSize(targetSize)
     154              17 :         , mClientID(clientID)
     155                 :     { 
     156              17 :         mClientIDSize = clientID ? strlen(clientID) : 0;
     157              17 :     }
     158                 :     
     159                 :     virtual PRInt32  VisitRecord(nsDiskCacheRecord *  mapRecord);
     160                 :  
     161                 : private:
     162                 :         nsDiskCacheMap *     mCacheMap;
     163                 :         nsDiskCacheBindery * mBindery;
     164                 :         PRUint32             mTargetSize;
     165                 :         const char *         mClientID;
     166                 :         PRUint32             mClientIDSize;
     167                 : };
     168                 : 
     169                 : 
     170                 : PRInt32
     171               1 : nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord *  mapRecord)
     172                 : {
     173               1 :     if (mCacheMap->TotalSize() < mTargetSize)
     174               0 :         return kStopVisitingRecords;
     175                 :     
     176               1 :     if (mClientID) {
     177                 :         // we're just evicting records for a specific client
     178               0 :         nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord);
     179               0 :         if (!diskEntry)
     180               0 :             return kVisitNextRecord;  // XXX or delete record?
     181                 :     
     182                 :         // Compare clientID's without malloc
     183               0 :         if ((diskEntry->mKeySize <= mClientIDSize) ||
     184               0 :             (diskEntry->Key()[mClientIDSize] != ':') ||
     185               0 :             (memcmp(diskEntry->Key(), mClientID, mClientIDSize) != 0)) {
     186               0 :             return kVisitNextRecord;  // clientID doesn't match, skip it
     187                 :         }
     188                 :     }
     189                 :     
     190               1 :     nsDiskCacheBinding * binding = mBindery->FindActiveBinding(mapRecord->HashNumber());
     191               1 :     if (binding) {
     192                 :         // If the entry is pending deactivation, cancel deactivation and doom
     193                 :         // the entry
     194               1 :         if (binding->mDeactivateEvent) {
     195               0 :             binding->mDeactivateEvent->CancelEvent();
     196               0 :             binding->mDeactivateEvent = nsnull;
     197                 :         }
     198                 :         // We are currently using this entry, so all we can do is doom it.
     199                 :         // Since we're enumerating the records, we don't want to call
     200                 :         // DeleteRecord when nsCacheService::DoomEntry() calls us back.
     201               1 :         binding->mDoomed = true;         // mark binding record as 'deleted'
     202               1 :         nsCacheService::DoomEntry(binding->mCacheEntry);
     203                 :     } else {
     204                 :         // entry not in use, just delete storage because we're enumerating the records
     205               0 :         (void) mCacheMap->DeleteStorage(mapRecord);
     206                 :     }
     207                 : 
     208               1 :     return kDeleteRecordAndContinue;  // this will REALLY delete the record
     209                 : }
     210                 : 
     211                 : 
     212                 : /******************************************************************************
     213                 :  *  nsDiskCacheDeviceInfo
     214                 :  *****************************************************************************/
     215                 : 
     216                 : class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo {
     217                 : public:
     218                 :     NS_DECL_ISUPPORTS
     219                 :     NS_DECL_NSICACHEDEVICEINFO
     220                 : 
     221               4 :     nsDiskCacheDeviceInfo(nsDiskCacheDevice* device)
     222               4 :         :   mDevice(device)
     223                 :     {
     224               4 :     }
     225                 : 
     226              16 :     virtual ~nsDiskCacheDeviceInfo() {}
     227                 :     
     228                 : private:
     229                 :     nsDiskCacheDevice* mDevice;
     230                 : };
     231                 : 
     232              68 : NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo, nsICacheDeviceInfo)
     233                 : 
     234                 : /* readonly attribute string description; */
     235               0 : NS_IMETHODIMP nsDiskCacheDeviceInfo::GetDescription(char ** aDescription)
     236                 : {
     237               0 :     NS_ENSURE_ARG_POINTER(aDescription);
     238               0 :     *aDescription = NS_strdup("Disk cache device");
     239               0 :     return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     240                 : }
     241                 : 
     242                 : /* readonly attribute string usageReport; */
     243               0 : NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport)
     244                 : {
     245               0 :     NS_ENSURE_ARG_POINTER(usageReport);
     246               0 :     nsCString buffer;
     247                 :     
     248                 :     buffer.AssignLiteral("  <tr>\n"
     249                 :                          "    <th>Cache Directory:</th>\n"
     250               0 :                          "    <td>");
     251               0 :     nsCOMPtr<nsILocalFile> cacheDir;
     252               0 :     nsAutoString           path;
     253               0 :     mDevice->getCacheDirectory(getter_AddRefs(cacheDir)); 
     254               0 :     nsresult rv = cacheDir->GetPath(path);
     255               0 :     if (NS_SUCCEEDED(rv)) {
     256               0 :         AppendUTF16toUTF8(path, buffer);
     257                 :     } else {
     258               0 :         buffer.AppendLiteral("directory unavailable");
     259                 :     }
     260                 :     buffer.AppendLiteral("</td>\n"
     261               0 :                          "  </tr>\n");
     262                 : 
     263               0 :     *usageReport = ToNewCString(buffer);
     264               0 :     if (!*usageReport) return NS_ERROR_OUT_OF_MEMORY;
     265                 : 
     266               0 :     return NS_OK;
     267                 : }
     268                 : 
     269                 : /* readonly attribute unsigned long entryCount; */
     270               0 : NS_IMETHODIMP nsDiskCacheDeviceInfo::GetEntryCount(PRUint32 *aEntryCount)
     271                 : {
     272               0 :     NS_ENSURE_ARG_POINTER(aEntryCount);
     273               0 :     *aEntryCount = mDevice->getEntryCount();
     274               0 :     return NS_OK;
     275                 : }
     276                 : 
     277                 : /* readonly attribute unsigned long totalSize; */
     278               2 : NS_IMETHODIMP nsDiskCacheDeviceInfo::GetTotalSize(PRUint32 *aTotalSize)
     279                 : {
     280               2 :     NS_ENSURE_ARG_POINTER(aTotalSize);
     281                 :     // Returned unit's are in bytes
     282               2 :     *aTotalSize = mDevice->getCacheSize() * 1024;
     283               2 :     return NS_OK;
     284                 : }
     285                 : 
     286                 : /* readonly attribute unsigned long maximumSize; */
     287               0 : NS_IMETHODIMP nsDiskCacheDeviceInfo::GetMaximumSize(PRUint32 *aMaximumSize)
     288                 : {
     289               0 :     NS_ENSURE_ARG_POINTER(aMaximumSize);
     290                 :     // Returned unit's are in bytes
     291               0 :     *aMaximumSize = mDevice->getCacheCapacity() * 1024;
     292               0 :     return NS_OK;
     293                 : }
     294                 : 
     295                 : 
     296                 : /******************************************************************************
     297                 :  *  nsDiskCache
     298                 :  *****************************************************************************/
     299                 : 
     300                 : /**
     301                 :  *  nsDiskCache::Hash(const char * key, PLDHashNumber initval)
     302                 :  *
     303                 :  *  See http://burtleburtle.net/bob/hash/evahash.html for more information
     304                 :  *  about this hash function.
     305                 :  *
     306                 :  *  This algorithm of this method implies nsDiskCacheRecords will be stored
     307                 :  *  in a certain order on disk.  If the algorithm changes, existing cache
     308                 :  *  map files may become invalid, and therefore the kCurrentVersion needs
     309                 :  *  to be revised.
     310                 :  */
     311                 : 
     312           13649 : static inline void hashmix(PRUint32& a, PRUint32& b, PRUint32& c)
     313                 : {
     314           13649 :   a -= b; a -= c; a ^= (c>>13);
     315           13649 :   b -= c; b -= a; b ^= (a<<8);
     316           13649 :   c -= a; c -= b; c ^= (b>>13);
     317           13649 :   a -= b; a -= c; a ^= (c>>12); 
     318           13649 :   b -= c; b -= a; b ^= (a<<16);
     319           13649 :   c -= a; c -= b; c ^= (b>>5);
     320           13649 :   a -= b; a -= c; a ^= (c>>3);
     321           13649 :   b -= c; b -= a; b ^= (a<<10);
     322           13649 :   c -= a; c -= b; c ^= (b>>15);
     323           13649 : }
     324                 : 
     325                 : PLDHashNumber
     326            2594 : nsDiskCache::Hash(const char * key, PLDHashNumber initval)
     327                 : {
     328            2594 :   const PRUint8 *k = reinterpret_cast<const PRUint8*>(key);
     329                 :   PRUint32 a, b, c, len, length;
     330                 : 
     331            2594 :   length = PL_strlen(key);
     332                 :   /* Set up the internal state */
     333            2594 :   len = length;
     334            2594 :   a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
     335            2594 :   c = initval;         /* variable initialization of internal state */
     336                 : 
     337                 :   /*---------------------------------------- handle most of the key */
     338           16243 :   while (len >= 12)
     339                 :   {
     340           11055 :     a += k[0] + (PRUint32(k[1])<<8) + (PRUint32(k[2])<<16) + (PRUint32(k[3])<<24);
     341           11055 :     b += k[4] + (PRUint32(k[5])<<8) + (PRUint32(k[6])<<16) + (PRUint32(k[7])<<24);
     342           11055 :     c += k[8] + (PRUint32(k[9])<<8) + (PRUint32(k[10])<<16) + (PRUint32(k[11])<<24);
     343           11055 :     hashmix(a, b, c);
     344           11055 :     k += 12; len -= 12;
     345                 :   }
     346                 : 
     347                 :   /*------------------------------------- handle the last 11 bytes */
     348            2594 :   c += length;
     349            2594 :   switch(len) {              /* all the case statements fall through */
     350             326 :     case 11: c += (PRUint32(k[10])<<24);
     351             553 :     case 10: c += (PRUint32(k[9])<<16);
     352             740 :     case 9 : c += (PRUint32(k[8])<<8);
     353                 :     /* the low-order byte of c is reserved for the length */
     354             830 :     case 8 : b += (PRUint32(k[7])<<24);
     355            1124 :     case 7 : b += (PRUint32(k[6])<<16);
     356            1397 :     case 6 : b += (PRUint32(k[5])<<8);
     357            1619 :     case 5 : b += k[4];
     358            1841 :     case 4 : a += (PRUint32(k[3])<<24);
     359            2030 :     case 3 : a += (PRUint32(k[2])<<16);
     360            2189 :     case 2 : a += (PRUint32(k[1])<<8);
     361            2400 :     case 1 : a += k[0];
     362                 :     /* case 0: nothing left to add */
     363                 :   }
     364            2594 :   hashmix(a, b, c);
     365                 : 
     366            2594 :   return c;
     367                 : }
     368                 : 
     369                 : nsresult
     370               4 : nsDiskCache::Truncate(PRFileDesc *  fd, PRUint32  newEOF)
     371                 : {
     372                 :     // use modified SetEOF from nsFileStreams::SetEOF()
     373                 : 
     374                 : #if defined(XP_UNIX)
     375               4 :     if (ftruncate(PR_FileDesc2NativeHandle(fd), newEOF) != 0) {
     376               0 :         NS_ERROR("ftruncate failed");
     377               0 :         return NS_ERROR_FAILURE;
     378                 :     }
     379                 : 
     380                 : #elif defined(XP_WIN)
     381                 :     PRInt32 cnt = PR_Seek(fd, newEOF, PR_SEEK_SET);
     382                 :     if (cnt == -1)  return NS_ERROR_FAILURE;
     383                 :     if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(fd))) {
     384                 :         NS_ERROR("SetEndOfFile failed");
     385                 :         return NS_ERROR_FAILURE;
     386                 :     }
     387                 : 
     388                 : #elif defined(XP_OS2)
     389                 :     if (DosSetFileSize((HFILE) PR_FileDesc2NativeHandle(fd), newEOF) != NO_ERROR) {
     390                 :         NS_ERROR("DosSetFileSize failed");
     391                 :         return NS_ERROR_FAILURE;
     392                 :     }
     393                 : #else
     394                 :     // add implementations for other platforms here
     395                 : #endif
     396               4 :     return NS_OK;
     397                 : }
     398                 : 
     399                 : 
     400                 : /******************************************************************************
     401                 :  *  nsDiskCacheDevice
     402                 :  *****************************************************************************/
     403                 : 
     404             175 : nsDiskCacheDevice::nsDiskCacheDevice()
     405                 :     : mCacheCapacity(0)
     406                 :     , mMaxEntrySize(-1) // -1 means "no limit"
     407             175 :     , mInitialized(false)
     408                 : {
     409             175 : }
     410                 : 
     411             525 : nsDiskCacheDevice::~nsDiskCacheDevice()
     412                 : {
     413             175 :     Shutdown();
     414             700 : }
     415                 : 
     416                 : 
     417                 : /**
     418                 :  *  methods of nsCacheDevice
     419                 :  */
     420                 : nsresult
     421             236 : nsDiskCacheDevice::Init()
     422                 : {
     423                 :     NS_TIME_FUNCTION;
     424                 : 
     425                 :     nsresult rv;
     426                 : 
     427             236 :     if (Initialized()) {
     428               0 :         NS_ERROR("Disk cache already initialized!");
     429               0 :         return NS_ERROR_UNEXPECTED;
     430                 :     }
     431                 :        
     432             236 :     if (!mCacheDirectory)
     433               0 :         return NS_ERROR_FAILURE;
     434                 : 
     435             236 :     rv = mBindery.Init();
     436             236 :     if (NS_FAILED(rv))
     437               0 :         return rv;
     438                 : 
     439             236 :     nsDeleteDir::RemoveOldTrashes(mCacheDirectory);
     440                 : 
     441                 :     // Open Disk Cache
     442             236 :     rv = OpenDiskCache();
     443             236 :     if (NS_FAILED(rv)) {
     444               0 :         (void) mCacheMap.Close(false);
     445               0 :         return rv;
     446                 :     }
     447                 : 
     448             236 :     mInitialized = true;
     449             236 :     return NS_OK;
     450                 : }
     451                 : 
     452                 : 
     453                 : /**
     454                 :  *  NOTE: called while holding the cache service lock
     455                 :  */
     456                 : nsresult
     457             350 : nsDiskCacheDevice::Shutdown()
     458                 : {
     459             350 :     nsCacheService::AssertOwnsLock();
     460                 : 
     461             350 :     nsresult rv = Shutdown_Private(true);
     462             350 :     if (NS_FAILED(rv))
     463               0 :         return rv;
     464                 : 
     465             350 :     return NS_OK;
     466                 : }
     467                 : 
     468                 : 
     469                 : nsresult
     470             411 : nsDiskCacheDevice::Shutdown_Private(bool    flush)
     471                 : {
     472             411 :     CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush));
     473                 : 
     474             411 :     if (Initialized()) {
     475                 :         // check cache limits in case we need to evict.
     476             236 :         EvictDiskCacheEntries(mCacheCapacity);
     477                 : 
     478                 :         // At this point there may be a number of pending cache-requests on the
     479                 :         // cache-io thread. Wait for all these to run before we wipe out our
     480                 :         // datastructures (see bug #620660)
     481             236 :         (void) nsCacheService::SyncWithCacheIOThread();
     482                 : 
     483                 :         // write out persistent information about the cache.
     484             236 :         (void) mCacheMap.Close(flush);
     485                 : 
     486             236 :         mBindery.Reset();
     487                 : 
     488             236 :         mInitialized = false;
     489                 :     }
     490                 : 
     491             411 :     return NS_OK;
     492                 : }
     493                 : 
     494                 : 
     495                 : const char *
     496             173 : nsDiskCacheDevice::GetDeviceID()
     497                 : {
     498             173 :     return DISK_CACHE_DEVICE_ID;
     499                 : }
     500                 : 
     501                 : /**
     502                 :  *  FindEntry -
     503                 :  *
     504                 :  *      cases:  key not in disk cache, hash number free
     505                 :  *              key not in disk cache, hash number used
     506                 :  *              key in disk cache
     507                 :  *
     508                 :  *  NOTE: called while holding the cache service lock
     509                 :  */
     510                 : nsCacheEntry *
     511            1428 : nsDiskCacheDevice::FindEntry(nsCString * key, bool *collision)
     512                 : {
     513            2856 :     Telemetry::AutoTimer<Telemetry::CACHE_DISK_SEARCH> timer;
     514            1428 :     if (!Initialized())  return nsnull;  // NS_ERROR_NOT_INITIALIZED
     515            1428 :     nsDiskCacheRecord       record;
     516            1428 :     nsDiskCacheBinding *    binding = nsnull;
     517            1428 :     PLDHashNumber           hashNumber = nsDiskCache::Hash(key->get());
     518                 : 
     519            1428 :     *collision = false;
     520                 : 
     521            1428 :     binding = mBindery.FindActiveBinding(hashNumber);
     522            1428 :     if (binding && !binding->mCacheEntry->Key()->Equals(*key)) {
     523               0 :         *collision = true;
     524               0 :         return nsnull;
     525            1428 :     } else if (binding && binding->mDeactivateEvent) {
     526               0 :         binding->mDeactivateEvent->CancelEvent();
     527               0 :         binding->mDeactivateEvent = nsnull;
     528               0 :         CACHE_LOG_DEBUG(("CACHE: reusing deactivated entry %p " \
     529                 :                          "req-key=%s  entry-key=%s\n",
     530                 :                          binding->mCacheEntry, key, binding->mCacheEntry->Key()));
     531                 : 
     532               0 :         return binding->mCacheEntry; // just return this one, observing that
     533                 :                                      // FindActiveBinding() does not return
     534                 :                                      // bindings to doomed entries
     535                 :     }
     536            1428 :     binding = nsnull;
     537                 : 
     538                 :     // lookup hash number in cache map
     539            1428 :     nsresult rv = mCacheMap.FindRecord(hashNumber, &record);
     540            1428 :     if (NS_FAILED(rv))  return nsnull;  // XXX log error?
     541                 :     
     542             279 :     nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record);
     543             279 :     if (!diskEntry) return nsnull;
     544                 :     
     545                 :     // compare key to be sure
     546             279 :     if (!key->Equals(diskEntry->Key())) {
     547               0 :         *collision = true;
     548               0 :         return nsnull;
     549                 :     }
     550                 :     
     551             279 :     nsCacheEntry * entry = diskEntry->CreateCacheEntry(this);
     552             279 :     if (!entry)  return nsnull;
     553                 :     
     554             279 :     binding = mBindery.CreateBinding(entry, &record);
     555             279 :     if (!binding) {
     556               0 :         delete entry;
     557               0 :         return nsnull;
     558                 :     }
     559                 :     
     560             279 :     return entry;
     561                 : }
     562                 : 
     563                 : 
     564                 : /**
     565                 :  *  NOTE: called while holding the cache service lock
     566                 :  */
     567                 : nsresult
     568             830 : nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry)
     569                 : {
     570             830 :     nsresult              rv = NS_OK;
     571             830 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     572             830 :     if (!IsValidBinding(binding))
     573               0 :         return NS_ERROR_UNEXPECTED;
     574                 : 
     575             830 :     CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n",
     576                 :         entry, binding->mRecord.HashNumber()));
     577                 : 
     578                 :     nsDiskCacheDeviceDeactivateEntryEvent *event =
     579             830 :         new nsDiskCacheDeviceDeactivateEntryEvent(this, entry, binding);
     580                 : 
     581                 :     // ensure we can cancel the event via the binding later if necessary
     582             830 :     binding->mDeactivateEvent = event;
     583                 : 
     584             830 :     rv = nsCacheService::DispatchToCacheIOThread(event);
     585             830 :     NS_ASSERTION(NS_SUCCEEDED(rv), "DeactivateEntry: Failed dispatching "
     586                 :                                    "deactivation event");
     587             830 :     return NS_OK;
     588                 : }
     589                 : 
     590                 : /**
     591                 :  *  NOTE: called while holding the cache service lock
     592                 :  */
     593                 : nsresult
     594             830 : nsDiskCacheDevice::DeactivateEntry_Private(nsCacheEntry * entry,
     595                 :                                            nsDiskCacheBinding * binding)
     596                 : {
     597             830 :     nsresult rv = NS_OK;
     598             830 :     if (entry->IsDoomed()) {
     599                 :         // delete data, entry, record from disk for entry
     600             170 :         rv = mCacheMap.DeleteStorage(&binding->mRecord);
     601                 : 
     602                 :     } else {
     603                 :         // save stuff to disk for entry
     604             660 :         rv = mCacheMap.WriteDiskCacheEntry(binding);
     605             660 :         if (NS_FAILED(rv)) {
     606                 :             // clean up as best we can
     607               0 :             (void) mCacheMap.DeleteStorage(&binding->mRecord);
     608               0 :             (void) mCacheMap.DeleteRecord(&binding->mRecord);
     609               0 :             binding->mDoomed = true; // record is no longer in cache map
     610                 :         }
     611                 :     }
     612                 : 
     613             830 :     mBindery.RemoveBinding(binding); // extract binding from collision detection stuff
     614             830 :     delete entry;   // which will release binding
     615             830 :     return rv;
     616                 : }
     617                 : 
     618                 : 
     619                 : /**
     620                 :  * BindEntry()
     621                 :  *      no hash number collision -> no problem
     622                 :  *      collision
     623                 :  *          record not active -> evict, no problem
     624                 :  *          record is active
     625                 :  *              record is already doomed -> record shouldn't have been in map, no problem
     626                 :  *              record is not doomed -> doom, and replace record in map
     627                 :  *              
     628                 :  *              walk matching hashnumber list to find lowest generation number
     629                 :  *              take generation number from other (data/meta) location,
     630                 :  *                  or walk active list
     631                 :  *
     632                 :  *  NOTE: called while holding the cache service lock
     633                 :  */
     634                 : nsresult
     635             551 : nsDiskCacheDevice::BindEntry(nsCacheEntry * entry)
     636                 : {
     637             551 :     if (!Initialized())  return  NS_ERROR_NOT_INITIALIZED;
     638             551 :     nsresult rv = NS_OK;
     639             551 :     nsDiskCacheRecord record, oldRecord;
     640                 :     nsDiskCacheBinding *binding;
     641             551 :     PLDHashNumber hashNumber = nsDiskCache::Hash(entry->Key()->get());
     642                 : 
     643                 :     // Find out if there is already an active binding for this hash. If yes it
     644                 :     // should have another key since BindEntry() shouldn't be called twice for
     645                 :     // the same entry. Doom the old entry, the new one will get another
     646                 :     // generation number so files won't collide.
     647             551 :     binding = mBindery.FindActiveBinding(hashNumber);
     648             551 :     if (binding) {
     649               0 :         NS_ASSERTION(!binding->mCacheEntry->Key()->Equals(*entry->Key()),
     650                 :                      "BindEntry called for already bound entry!");
     651                 :         // If the entry is pending deactivation, cancel deactivation
     652               0 :         if (binding->mDeactivateEvent) {
     653               0 :             binding->mDeactivateEvent->CancelEvent();
     654               0 :             binding->mDeactivateEvent = nsnull;
     655                 :         }
     656               0 :         nsCacheService::DoomEntry(binding->mCacheEntry);
     657               0 :         binding = nsnull;
     658                 :     }
     659                 : 
     660                 :     // Lookup hash number in cache map. There can be a colliding inactive entry.
     661                 :     // See bug #321361 comment 21 for the scenario. If there is such entry,
     662                 :     // delete it.
     663             551 :     rv = mCacheMap.FindRecord(hashNumber, &record);
     664             551 :     if (NS_SUCCEEDED(rv)) {
     665               0 :         nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record);
     666               0 :         if (diskEntry) {
     667                 :             // compare key to be sure
     668               0 :             if (!entry->Key()->Equals(diskEntry->Key())) {
     669               0 :                 mCacheMap.DeleteStorage(&record);
     670               0 :                 rv = mCacheMap.DeleteRecord(&record);
     671               0 :                 if (NS_FAILED(rv))  return rv;
     672                 :             }
     673                 :         }
     674               0 :         record = nsDiskCacheRecord();
     675                 :     }
     676                 : 
     677                 :     // create a new record for this entry
     678             551 :     record.SetHashNumber(nsDiskCache::Hash(entry->Key()->get()));
     679             551 :     record.SetEvictionRank(ULONG_MAX - SecondsFromPRTime(PR_Now()));
     680                 : 
     681             551 :     CACHE_LOG_DEBUG(("CACHE: disk BindEntry [%p %x]\n",
     682                 :         entry, record.HashNumber()));
     683                 : 
     684             551 :     if (!entry->IsDoomed()) {
     685                 :         // if entry isn't doomed, add it to the cache map
     686             551 :         rv = mCacheMap.AddRecord(&record, &oldRecord); // deletes old record, if any
     687             551 :         if (NS_FAILED(rv))  return rv;
     688                 :         
     689             551 :         PRUint32    oldHashNumber = oldRecord.HashNumber();
     690             551 :         if (oldHashNumber) {
     691                 :             // gotta evict this one first
     692               0 :             nsDiskCacheBinding * oldBinding = mBindery.FindActiveBinding(oldHashNumber);
     693               0 :             if (oldBinding) {
     694                 :                 // XXX if debug : compare keys for hashNumber collision
     695                 : 
     696               0 :                 if (!oldBinding->mCacheEntry->IsDoomed()) {
     697                 :                     // If the old entry is pending deactivation, cancel deactivation
     698               0 :                     if (oldBinding->mDeactivateEvent) {
     699               0 :                         oldBinding->mDeactivateEvent->CancelEvent();
     700               0 :                         oldBinding->mDeactivateEvent = nsnull;
     701                 :                     }
     702                 :                 // we've got a live one!
     703               0 :                     nsCacheService::DoomEntry(oldBinding->mCacheEntry);
     704                 :                     // storage will be delete when oldBinding->mCacheEntry is Deactivated
     705                 :                 }
     706                 :             } else {
     707                 :                 // delete storage
     708                 :                 // XXX if debug : compare keys for hashNumber collision
     709               0 :                 rv = mCacheMap.DeleteStorage(&oldRecord);
     710               0 :                 if (NS_FAILED(rv))  return rv;  // XXX delete record we just added?
     711                 :             }
     712                 :         }
     713                 :     }
     714                 :     
     715                 :     // Make sure this entry has its associated nsDiskCacheBinding attached.
     716             551 :     binding = mBindery.CreateBinding(entry, &record);
     717             551 :     NS_ASSERTION(binding, "nsDiskCacheDevice::BindEntry");
     718             551 :     if (!binding) return NS_ERROR_OUT_OF_MEMORY;
     719             551 :     NS_ASSERTION(binding->mRecord.ValidRecord(), "bad cache map record");
     720                 : 
     721             551 :     return NS_OK;
     722                 : }
     723                 : 
     724                 : 
     725                 : /**
     726                 :  *  NOTE: called while holding the cache service lock
     727                 :  */
     728                 : void
     729             170 : nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry)
     730                 : {
     731             170 :     CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry));
     732                 : 
     733             170 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     734             170 :     NS_ASSERTION(binding, "DoomEntry: binding == nsnull");
     735             170 :     if (!binding)
     736               0 :         return;
     737                 : 
     738             170 :     if (!binding->mDoomed) {
     739                 :         // so it can't be seen by FindEntry() ever again.
     740                 : #ifdef DEBUG
     741                 :         nsresult rv =
     742                 : #endif
     743             169 :             mCacheMap.DeleteRecord(&binding->mRecord);
     744             169 :         NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed.");
     745             169 :         binding->mDoomed = true; // record in no longer in cache map
     746                 :     }
     747                 : }
     748                 : 
     749                 : 
     750                 : /**
     751                 :  *  NOTE: called while holding the cache service lock
     752                 :  */
     753                 : nsresult
     754             172 : nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry *      entry,
     755                 :                                            nsCacheAccessMode   mode, 
     756                 :                                            PRUint32            offset,
     757                 :                                            nsIInputStream **   result)
     758                 : {
     759             172 :     CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n",
     760                 :         entry, mode, offset));
     761                 : 
     762             172 :     NS_ENSURE_ARG_POINTER(entry);
     763             172 :     NS_ENSURE_ARG_POINTER(result);
     764                 : 
     765                 :     nsresult             rv;
     766             172 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     767             172 :     if (!IsValidBinding(binding))
     768               0 :         return NS_ERROR_UNEXPECTED;
     769                 : 
     770             172 :     NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other");
     771                 : 
     772             172 :     rv = binding->EnsureStreamIO();
     773             172 :     if (NS_FAILED(rv)) return rv;
     774                 : 
     775             172 :     return binding->mStreamIO->GetInputStream(offset, result);
     776                 : }
     777                 : 
     778                 : 
     779                 : /**
     780                 :  *  NOTE: called while holding the cache service lock
     781                 :  */
     782                 : nsresult
     783             557 : nsDiskCacheDevice::OpenOutputStreamForEntry(nsCacheEntry *      entry,
     784                 :                                             nsCacheAccessMode   mode, 
     785                 :                                             PRUint32            offset,
     786                 :                                             nsIOutputStream **  result)
     787                 : {
     788             557 :     CACHE_LOG_DEBUG(("CACHE: disk OpenOutputStreamForEntry [%p %x %u]\n",
     789                 :         entry, mode, offset));
     790                 :  
     791             557 :     NS_ENSURE_ARG_POINTER(entry);
     792             557 :     NS_ENSURE_ARG_POINTER(result);
     793                 : 
     794                 :     nsresult             rv;
     795             557 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     796             557 :     if (!IsValidBinding(binding))
     797               0 :         return NS_ERROR_UNEXPECTED;
     798                 :     
     799             557 :     NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other");
     800                 : 
     801             557 :     rv = binding->EnsureStreamIO();
     802             557 :     if (NS_FAILED(rv)) return rv;
     803                 : 
     804             557 :     return binding->mStreamIO->GetOutputStream(offset, result);
     805                 : }
     806                 : 
     807                 : 
     808                 : /**
     809                 :  *  NOTE: called while holding the cache service lock
     810                 :  */
     811                 : nsresult
     812               0 : nsDiskCacheDevice::GetFileForEntry(nsCacheEntry *    entry,
     813                 :                                    nsIFile **        result)
     814                 : {
     815               0 :     NS_ENSURE_ARG_POINTER(result);
     816               0 :     *result = nsnull;
     817                 : 
     818                 :     nsresult             rv;
     819                 :         
     820               0 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     821               0 :     if (!IsValidBinding(binding))
     822               0 :         return NS_ERROR_UNEXPECTED;
     823                 : 
     824                 :     // check/set binding->mRecord for separate file, sync w/mCacheMap
     825               0 :     if (binding->mRecord.DataLocationInitialized()) {
     826               0 :         if (binding->mRecord.DataFile() != 0)
     827               0 :             return NS_ERROR_NOT_AVAILABLE;  // data not stored as separate file
     828                 : 
     829               0 :         NS_ASSERTION(binding->mRecord.DataFileGeneration() == binding->mGeneration, "error generations out of sync");
     830                 :     } else {
     831               0 :         binding->mRecord.SetDataFileGeneration(binding->mGeneration);
     832               0 :         binding->mRecord.SetDataFileSize(0);    // 1k minimum
     833               0 :         if (!binding->mDoomed) {
     834                 :             // record stored in cache map, so update it
     835               0 :             rv = mCacheMap.UpdateRecord(&binding->mRecord);
     836               0 :             if (NS_FAILED(rv))  return rv;
     837                 :         }
     838                 :     }
     839                 :     
     840               0 :     nsCOMPtr<nsIFile>  file;
     841                 :     rv = mCacheMap.GetFileForDiskCacheRecord(&binding->mRecord,
     842                 :                                              nsDiskCache::kData,
     843                 :                                              false,
     844               0 :                                              getter_AddRefs(file));
     845               0 :     if (NS_FAILED(rv))  return rv;
     846                 :     
     847               0 :     NS_IF_ADDREF(*result = file);
     848               0 :     return NS_OK;
     849                 : }
     850                 : 
     851                 : 
     852                 : /**
     853                 :  *  This routine will get called every time an open descriptor is written to.
     854                 :  *
     855                 :  *  NOTE: called while holding the cache service lock
     856                 :  */
     857                 : nsresult
     858            1315 : nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize)
     859                 : {
     860            1315 :     CACHE_LOG_DEBUG(("CACHE: disk OnDataSizeChange [%p %d]\n",
     861                 :         entry, deltaSize));
     862                 : 
     863                 :     // If passed a negative value, then there's nothing to do.
     864            1315 :     if (deltaSize < 0)
     865               6 :         return NS_OK;
     866                 : 
     867            1309 :     nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
     868            1309 :     if (!IsValidBinding(binding))
     869               0 :         return NS_ERROR_UNEXPECTED;
     870                 : 
     871            1309 :     NS_ASSERTION(binding->mRecord.ValidRecord(), "bad record");
     872                 : 
     873            1309 :     PRUint32  newSize = entry->DataSize() + deltaSize;
     874            1309 :     PRUint32  newSizeK =  ((newSize + 0x3FF) >> 10);
     875                 : 
     876                 :     // If the new size is larger than max. file size or larger than
     877                 :     // 1/8 the cache capacity (which is in KiB's), and the entry has
     878                 :     // not been marked for file storage, doom the entry and abort.
     879            1371 :     if (EntryIsTooBig(newSize) &&
     880              62 :         entry->StoragePolicy() != nsICache::STORE_ON_DISK_AS_FILE) {
     881                 : #ifdef DEBUG
     882                 :         nsresult rv =
     883                 : #endif
     884               2 :             nsCacheService::DoomEntry(entry);
     885               2 :         NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
     886               2 :         return NS_ERROR_ABORT;
     887                 :     }
     888                 : 
     889            1307 :     PRUint32  sizeK = ((entry->DataSize() + 0x03FF) >> 10); // round up to next 1k
     890                 : 
     891                 :     // In total count we ignore anything over kMaxDataSizeK (bug #651100), so
     892                 :     // the target capacity should be calculated the same way.
     893            1307 :     if (sizeK > kMaxDataSizeK) sizeK = kMaxDataSizeK;
     894            1307 :     if (newSizeK > kMaxDataSizeK) newSizeK = kMaxDataSizeK;
     895                 : 
     896                 :     // pre-evict entries to make space for new data
     897                 :     PRUint32  targetCapacity = mCacheCapacity > (newSizeK - sizeK)
     898                 :                              ? mCacheCapacity - (newSizeK - sizeK)
     899            1307 :                              : 0;
     900            1307 :     EvictDiskCacheEntries(targetCapacity);
     901                 :     
     902            1307 :     return NS_OK;
     903                 : }
     904                 : 
     905                 : 
     906                 : /******************************************************************************
     907                 :  *  EntryInfoVisitor
     908                 :  *****************************************************************************/
     909                 : class EntryInfoVisitor : public nsDiskCacheRecordVisitor
     910                 : {
     911                 : public:
     912               0 :     EntryInfoVisitor(nsDiskCacheMap *    cacheMap,
     913                 :                      nsICacheVisitor *   visitor)
     914                 :         : mCacheMap(cacheMap)
     915               0 :         , mVisitor(visitor)
     916               0 :     {}
     917                 :     
     918               0 :     virtual PRInt32  VisitRecord(nsDiskCacheRecord *  mapRecord)
     919                 :     {
     920                 :         // XXX optimization: do we have this record in memory?
     921                 :         
     922                 :         // read in the entry (metadata)
     923               0 :         nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord);
     924               0 :         if (!diskEntry) {
     925               0 :             return kVisitNextRecord;
     926                 :         }
     927                 : 
     928                 :         // create nsICacheEntryInfo
     929               0 :         nsDiskCacheEntryInfo * entryInfo = new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID, diskEntry);
     930               0 :         if (!entryInfo) {
     931               0 :             return kStopVisitingRecords;
     932                 :         }
     933               0 :         nsCOMPtr<nsICacheEntryInfo> ref(entryInfo);
     934                 :         
     935                 :         bool    keepGoing;
     936               0 :         (void)mVisitor->VisitEntry(DISK_CACHE_DEVICE_ID, entryInfo, &keepGoing);
     937               0 :         return keepGoing ? kVisitNextRecord : kStopVisitingRecords;
     938                 :     }
     939                 :  
     940                 : private:
     941                 :         nsDiskCacheMap *    mCacheMap;
     942                 :         nsICacheVisitor *   mVisitor;
     943                 : };
     944                 : 
     945                 : 
     946                 : nsresult
     947               4 : nsDiskCacheDevice::Visit(nsICacheVisitor * visitor)
     948                 : {
     949               4 :     if (!Initialized())  return NS_ERROR_NOT_INITIALIZED;
     950               4 :     nsDiskCacheDeviceInfo* deviceInfo = new nsDiskCacheDeviceInfo(this);
     951               8 :     nsCOMPtr<nsICacheDeviceInfo> ref(deviceInfo);
     952                 :     
     953                 :     bool keepGoing;
     954               4 :     nsresult rv = visitor->VisitDevice(DISK_CACHE_DEVICE_ID, deviceInfo, &keepGoing);
     955               4 :     if (NS_FAILED(rv)) return rv;
     956                 :     
     957               4 :     if (keepGoing) {
     958               0 :         EntryInfoVisitor  infoVisitor(&mCacheMap, visitor);
     959               0 :         return mCacheMap.VisitRecords(&infoVisitor);
     960                 :     }
     961                 : 
     962               4 :     return NS_OK;
     963                 : }
     964                 : 
     965                 : // Max allowed size for an entry is currently MIN(mMaxEntrySize, 1/8 CacheCapacity)
     966                 : bool
     967            1843 : nsDiskCacheDevice::EntryIsTooBig(PRInt64 entrySize)
     968                 : {
     969            1843 :     if (mMaxEntrySize == -1) // no limit
     970               3 :         return entrySize > (static_cast<PRInt64>(mCacheCapacity) * 1024 / 8);
     971                 :     else 
     972                 :         return entrySize > mMaxEntrySize ||
     973            1840 :                entrySize > (static_cast<PRInt64>(mCacheCapacity) * 1024 / 8);
     974                 : }
     975                 : 
     976                 : nsresult
     977              61 : nsDiskCacheDevice::EvictEntries(const char * clientID)
     978                 : {
     979              61 :     CACHE_LOG_DEBUG(("CACHE: disk EvictEntries [%s]\n", clientID));
     980                 : 
     981              61 :     if (!Initialized())  return NS_ERROR_NOT_INITIALIZED;
     982                 :     nsresult  rv;
     983                 : 
     984              61 :     if (clientID == nsnull) {
     985                 :         // we're clearing the entire disk cache
     986              61 :         rv = ClearDiskCache();
     987              61 :         if (rv != NS_ERROR_CACHE_IN_USE)
     988              61 :             return rv;
     989                 :     }
     990                 : 
     991               0 :     nsDiskCacheEvictor  evictor(&mCacheMap, &mBindery, 0, clientID);
     992               0 :     rv = mCacheMap.VisitRecords(&evictor);
     993                 :     
     994               0 :     if (clientID == nsnull)     // we tried to clear the entire cache
     995               0 :         rv = mCacheMap.Trim(); // so trim cache block files (if possible)
     996               0 :     return rv;
     997                 : }
     998                 : 
     999                 : 
    1000                 : /**
    1001                 :  *  private methods
    1002                 :  */
    1003                 : 
    1004                 : nsresult
    1005             236 : nsDiskCacheDevice::OpenDiskCache()
    1006                 : {
    1007             472 :     Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_OPEN> timer;
    1008                 :     // if we don't have a cache directory, create one and open it
    1009                 :     bool exists;
    1010             236 :     nsresult rv = mCacheDirectory->Exists(&exists);
    1011             236 :     if (NS_FAILED(rv))
    1012               0 :         return rv;
    1013                 : 
    1014             236 :     if (exists) {
    1015                 :         // Try opening cache map file.
    1016               0 :         rv = mCacheMap.Open(mCacheDirectory);        
    1017                 :         // move "corrupt" caches to trash
    1018               0 :         if (rv == NS_ERROR_FILE_CORRUPTED) {
    1019                 :             // delay delete by 1 minute to avoid IO thrash at startup
    1020               0 :             rv = nsDeleteDir::DeleteDir(mCacheDirectory, true, 60000);
    1021               0 :             if (NS_FAILED(rv))
    1022               0 :                 return rv;
    1023               0 :             exists = false;
    1024                 :         }
    1025               0 :         else if (NS_FAILED(rv))
    1026               0 :             return rv;
    1027                 :     }
    1028                 : 
    1029                 :     // if we don't have a cache directory, create one and open it
    1030             236 :     if (!exists) {
    1031             236 :         rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
    1032             236 :         CACHE_LOG_PATH(PR_LOG_ALWAYS, "\ncreate cache directory: %s\n", mCacheDirectory);
    1033             236 :         CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv));
    1034             236 :         if (NS_FAILED(rv))
    1035               0 :             return rv;
    1036                 :     
    1037                 :         // reopen the cache map     
    1038             236 :         rv = mCacheMap.Open(mCacheDirectory);
    1039             236 :         if (NS_FAILED(rv))
    1040               0 :             return rv;
    1041                 :     }
    1042                 : 
    1043             236 :     return NS_OK;
    1044                 : }
    1045                 : 
    1046                 : 
    1047                 : nsresult
    1048              61 : nsDiskCacheDevice::ClearDiskCache()
    1049                 : {
    1050              61 :     if (mBindery.ActiveBindings())
    1051               0 :         return NS_ERROR_CACHE_IN_USE;
    1052                 : 
    1053              61 :     nsresult rv = Shutdown_Private(false);  // false: don't bother flushing
    1054              61 :     if (NS_FAILED(rv))
    1055               0 :         return rv;
    1056                 : 
    1057                 :     // If the disk cache directory is already gone, then it's not an error if
    1058                 :     // we fail to delete it ;-)
    1059              61 :     rv = nsDeleteDir::DeleteDir(mCacheDirectory, true);
    1060              61 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
    1061               0 :         return rv;
    1062                 : 
    1063              61 :     return Init();
    1064                 : }
    1065                 : 
    1066                 : 
    1067                 : nsresult
    1068            1544 : nsDiskCacheDevice::EvictDiskCacheEntries(PRUint32  targetCapacity)
    1069                 : {
    1070            1544 :     CACHE_LOG_DEBUG(("CACHE: disk EvictDiskCacheEntries [%u]\n",
    1071                 :         targetCapacity));
    1072                 : 
    1073            1544 :     NS_ASSERTION(targetCapacity > 0, "oops");
    1074                 : 
    1075            1544 :     if (mCacheMap.TotalSize() < targetCapacity)
    1076            1527 :         return NS_OK;
    1077                 : 
    1078                 :     // targetCapacity is in KiB's
    1079              17 :     nsDiskCacheEvictor  evictor(&mCacheMap, &mBindery, targetCapacity, nsnull);
    1080              17 :     return mCacheMap.EvictRecords(&evictor);
    1081                 : }
    1082                 : 
    1083                 : 
    1084                 : /**
    1085                 :  *  methods for prefs
    1086                 :  */
    1087                 : 
    1088                 : void
    1089             175 : nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile * parentDir)
    1090                 : {
    1091                 :     nsresult rv;
    1092                 :     bool    exists;
    1093                 : 
    1094             175 :     if (Initialized()) {
    1095               0 :         NS_ASSERTION(false, "Cannot switch cache directory when initialized");
    1096               0 :         return;
    1097                 :     }
    1098                 : 
    1099             175 :     if (!parentDir) {
    1100               0 :         mCacheDirectory = nsnull;
    1101               0 :         return;
    1102                 :     }
    1103                 : 
    1104                 :     // ensure parent directory exists
    1105             175 :     rv = parentDir->Exists(&exists);
    1106             175 :     if (NS_SUCCEEDED(rv) && !exists)
    1107               0 :         rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
    1108             175 :     if (NS_FAILED(rv))  return;
    1109                 : 
    1110                 :     // ensure cache directory exists
    1111             350 :     nsCOMPtr<nsIFile> directory;
    1112                 :     
    1113             175 :     rv = parentDir->Clone(getter_AddRefs(directory));
    1114             175 :     if (NS_FAILED(rv))  return;
    1115             175 :     rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
    1116             175 :     if (NS_FAILED(rv))  return;
    1117                 :     
    1118             175 :     mCacheDirectory = do_QueryInterface(directory);
    1119                 : }
    1120                 : 
    1121                 : 
    1122                 : void
    1123               0 : nsDiskCacheDevice::getCacheDirectory(nsILocalFile ** result)
    1124                 : {
    1125               0 :     *result = mCacheDirectory;
    1126               0 :     NS_IF_ADDREF(*result);
    1127               0 : }
    1128                 : 
    1129                 : 
    1130                 : /**
    1131                 :  *  NOTE: called while holding the cache service lock
    1132                 :  */
    1133                 : void
    1134             176 : nsDiskCacheDevice::SetCapacity(PRUint32  capacity)
    1135                 : {
    1136                 :     // Units are KiB's
    1137             176 :     mCacheCapacity = capacity;
    1138             176 :     if (Initialized()) {
    1139               1 :         if (NS_IsMainThread()) {
    1140                 :             // Do not evict entries on the main thread
    1141                 :             nsCacheService::DispatchToCacheIOThread(
    1142               1 :                 new nsEvictDiskCacheEntriesEvent(this));
    1143                 :         } else {
    1144                 :             // start evicting entries if the new size is smaller!
    1145               0 :             EvictDiskCacheEntries(mCacheCapacity);
    1146                 :         }
    1147                 :     }
    1148                 :     // Let cache map know of the new capacity
    1149             176 :     mCacheMap.NotifyCapacityChange(capacity);
    1150             176 : }
    1151                 : 
    1152                 : 
    1153               0 : PRUint32 nsDiskCacheDevice::getCacheCapacity()
    1154                 : {
    1155               0 :     return mCacheCapacity;
    1156                 : }
    1157                 : 
    1158                 : 
    1159               2 : PRUint32 nsDiskCacheDevice::getCacheSize()
    1160                 : {
    1161               2 :     return mCacheMap.TotalSize();
    1162                 : }
    1163                 : 
    1164                 : 
    1165               0 : PRUint32 nsDiskCacheDevice::getEntryCount()
    1166                 : {
    1167               0 :     return mCacheMap.EntryCount();
    1168                 : }
    1169                 : 
    1170                 : void
    1171             177 : nsDiskCacheDevice::SetMaxEntrySize(PRInt32 maxSizeInKilobytes)
    1172                 : {
    1173                 :     // Internal units are bytes. Changing this only takes effect *after* the
    1174                 :     // change and has no consequences for existing cache-entries
    1175             177 :     if (maxSizeInKilobytes >= 0)
    1176             176 :         mMaxEntrySize = maxSizeInKilobytes * 1024;
    1177                 :     else
    1178               1 :         mMaxEntrySize = -1;
    1179             177 : }

Generated by: LCOV version 1.7