1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is nsDiskCacheDevice.cpp, released
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.
25 * Gordon Sheridan <gordon@netscape.com>
26 * Patrick C. Beard <beard@netscape.com>
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.
40 * ***** END LICENSE BLOCK ***** */
44 // include files for ftruncate (or equivalent)
45 #if defined(XP_UNIX) || defined(XP_BEOS)
50 #define INCL_DOSERRORS
53 // XXX add necessary include file for ftruncate (or equivalent)
60 #include "private/pprio.h"
62 #include "nsDiskCacheDevice.h"
63 #include "nsDiskCacheEntry.h"
64 #include "nsDiskCacheMap.h"
65 #include "nsDiskCacheStreams.h"
67 #include "nsDiskCache.h"
69 #include "nsCacheService.h"
72 #include "nsDeleteDir.h"
74 #include "nsICacheVisitor.h"
75 #include "nsReadableUtils.h"
76 #include "nsIInputStream.h"
77 #include "nsIOutputStream.h"
78 #include "nsAutoLock.h"
80 #include "nsCOMArray.h"
81 #include "nsISimpleEnumerator.h"
83 static const char DISK_CACHE_DEVICE_ID
[] = { "disk" };
86 /******************************************************************************
89 * Helper class for nsDiskCacheDevice.
91 *****************************************************************************/
93 class nsDiskCacheEvictor
: public nsDiskCacheRecordVisitor
96 nsDiskCacheEvictor( nsDiskCacheMap
* cacheMap
,
97 nsDiskCacheBindery
* cacheBindery
,
99 const char * clientID
)
100 : mCacheMap(cacheMap
)
101 , mBindery(cacheBindery
)
102 , mTargetSize(targetSize
)
103 , mClientID(clientID
)
105 mClientIDSize
= clientID
? strlen(clientID
) : 0;
108 virtual PRInt32
VisitRecord(nsDiskCacheRecord
* mapRecord
);
111 nsDiskCacheMap
* mCacheMap
;
112 nsDiskCacheBindery
* mBindery
;
113 PRUint32 mTargetSize
;
114 const char * mClientID
;
115 PRUint32 mClientIDSize
;
120 nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord
* mapRecord
)
122 if (mCacheMap
->TotalSize() < mTargetSize
)
123 return kStopVisitingRecords
;
126 // we're just evicting records for a specific client
127 nsDiskCacheEntry
* diskEntry
= mCacheMap
->ReadDiskCacheEntry(mapRecord
);
129 return kVisitNextRecord
; // XXX or delete record?
131 // Compare clientID's without malloc
132 if ((diskEntry
->mKeySize
<= mClientIDSize
) ||
133 (diskEntry
->Key()[mClientIDSize
] != ':') ||
134 (memcmp(diskEntry
->Key(), mClientID
, mClientIDSize
) != 0)) {
135 return kVisitNextRecord
; // clientID doesn't match, skip it
139 nsDiskCacheBinding
* binding
= mBindery
->FindActiveBinding(mapRecord
->HashNumber());
141 // We are currently using this entry, so all we can do is doom it.
142 // Since we're enumerating the records, we don't want to call
143 // DeleteRecord when nsCacheService::DoomEntry() calls us back.
144 binding
->mDoomed
= PR_TRUE
; // mark binding record as 'deleted'
145 nsCacheService::DoomEntry(binding
->mCacheEntry
);
147 // entry not in use, just delete storage because we're enumerating the records
148 (void) mCacheMap
->DeleteStorage(mapRecord
);
151 return kDeleteRecordAndContinue
; // this will REALLY delete the record
155 /******************************************************************************
156 * nsDiskCacheDeviceInfo
157 *****************************************************************************/
159 class nsDiskCacheDeviceInfo
: public nsICacheDeviceInfo
{
162 NS_DECL_NSICACHEDEVICEINFO
164 nsDiskCacheDeviceInfo(nsDiskCacheDevice
* device
)
169 virtual ~nsDiskCacheDeviceInfo() {}
172 nsDiskCacheDevice
* mDevice
;
175 NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo
, nsICacheDeviceInfo
)
177 /* readonly attribute string description; */
178 NS_IMETHODIMP
nsDiskCacheDeviceInfo::GetDescription(char ** aDescription
)
180 NS_ENSURE_ARG_POINTER(aDescription
);
181 *aDescription
= NS_strdup("Disk cache device");
182 return *aDescription
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
185 /* readonly attribute string usageReport; */
186 NS_IMETHODIMP
nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport
)
188 NS_ENSURE_ARG_POINTER(usageReport
);
191 buffer
.AssignLiteral("\n<tr>\n<td><b>Cache Directory:</b></td>\n<td><tt> ");
192 nsCOMPtr
<nsILocalFile
> cacheDir
;
194 mDevice
->getCacheDirectory(getter_AddRefs(cacheDir
));
195 nsresult rv
= cacheDir
->GetPath(path
);
196 if (NS_SUCCEEDED(rv
)) {
197 AppendUTF16toUTF8(path
, buffer
);
199 buffer
.AppendLiteral("directory unavailable");
201 buffer
.AppendLiteral("</tt></td>\n</tr>\n");
202 // buffer.Append("<tr><td><b>Files:</b></td><td><tt> XXX</tt></td></tr>");
203 *usageReport
= ToNewCString(buffer
);
204 if (!*usageReport
) return NS_ERROR_OUT_OF_MEMORY
;
209 /* readonly attribute unsigned long entryCount; */
210 NS_IMETHODIMP
nsDiskCacheDeviceInfo::GetEntryCount(PRUint32
*aEntryCount
)
212 NS_ENSURE_ARG_POINTER(aEntryCount
);
213 *aEntryCount
= mDevice
->getEntryCount();
217 /* readonly attribute unsigned long totalSize; */
218 NS_IMETHODIMP
nsDiskCacheDeviceInfo::GetTotalSize(PRUint32
*aTotalSize
)
220 NS_ENSURE_ARG_POINTER(aTotalSize
);
221 // Returned unit's are in bytes
222 *aTotalSize
= mDevice
->getCacheSize() * 1024;
226 /* readonly attribute unsigned long maximumSize; */
227 NS_IMETHODIMP
nsDiskCacheDeviceInfo::GetMaximumSize(PRUint32
*aMaximumSize
)
229 NS_ENSURE_ARG_POINTER(aMaximumSize
);
230 // Returned unit's are in bytes
231 *aMaximumSize
= mDevice
->getCacheCapacity() * 1024;
236 /******************************************************************************
238 *****************************************************************************/
241 * nsDiskCache::Hash(const char * key)
243 * This algorithm of this method implies nsDiskCacheRecords will be stored
244 * in a certain order on disk. If the algorithm changes, existing cache
245 * map files may become invalid, and therefore the kCurrentVersion needs
249 nsDiskCache::Hash(const char * key
)
252 for (const PRUint8
* s
= (PRUint8
*) key
; *s
!= '\0'; ++s
)
253 h
= PR_ROTATE_LEFT32(h
, 4) ^ *s
;
254 return (h
== 0 ? ULONG_MAX
: h
);
259 nsDiskCache::Truncate(PRFileDesc
* fd
, PRUint32 newEOF
)
261 // use modified SetEOF from nsFileStreams::SetEOF()
263 #if defined(XP_UNIX) || defined(XP_BEOS)
264 if (ftruncate(PR_FileDesc2NativeHandle(fd
), newEOF
) != 0) {
265 NS_ERROR("ftruncate failed");
266 return NS_ERROR_FAILURE
;
269 #elif defined(XP_WIN)
270 PRInt32 cnt
= PR_Seek(fd
, newEOF
, PR_SEEK_SET
);
271 if (cnt
== -1) return NS_ERROR_FAILURE
;
272 if (!SetEndOfFile((HANDLE
) PR_FileDesc2NativeHandle(fd
))) {
273 NS_ERROR("SetEndOfFile failed");
274 return NS_ERROR_FAILURE
;
277 #elif defined(XP_OS2)
278 if (DosSetFileSize((HFILE
) PR_FileDesc2NativeHandle(fd
), newEOF
) != NO_ERROR
) {
279 NS_ERROR("DosSetFileSize failed");
280 return NS_ERROR_FAILURE
;
283 // add implementations for other platforms here
289 /******************************************************************************
291 *****************************************************************************/
293 nsDiskCacheDevice::nsDiskCacheDevice()
295 , mInitialized(PR_FALSE
)
299 nsDiskCacheDevice::~nsDiskCacheDevice()
306 * methods of nsCacheDevice
309 nsDiskCacheDevice::Init()
313 NS_ENSURE_TRUE(!Initialized(), NS_ERROR_FAILURE
);
315 if (!mCacheDirectory
)
316 return NS_ERROR_FAILURE
;
318 rv
= mBindery
.Init();
323 rv
= OpenDiskCache();
325 (void) mCacheMap
.Close(PR_FALSE
);
329 mInitialized
= PR_TRUE
;
335 * NOTE: called while holding the cache service lock
338 nsDiskCacheDevice::Shutdown()
340 nsresult rv
= Shutdown_Private(PR_TRUE
);
344 if (mCacheDirectory
) {
345 // delete any trash files left-over before shutting down.
346 nsCOMPtr
<nsIFile
> trashDir
;
347 GetTrashDir(mCacheDirectory
, &trashDir
);
350 if (NS_SUCCEEDED(trashDir
->Exists(&exists
)) && exists
)
351 DeleteDir(trashDir
, PR_FALSE
, PR_TRUE
);
360 nsDiskCacheDevice::Shutdown_Private(PRBool flush
)
362 CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush
));
365 // check cache limits in case we need to evict.
366 EvictDiskCacheEntries(mCacheCapacity
);
368 // write out persistent information about the cache.
369 (void) mCacheMap
.Close(flush
);
373 mInitialized
= PR_FALSE
;
381 nsDiskCacheDevice::GetDeviceID()
383 return DISK_CACHE_DEVICE_ID
;
390 * cases: key not in disk cache, hash number free
391 * key not in disk cache, hash number used
394 * NOTE: called while holding the cache service lock
397 nsDiskCacheDevice::FindEntry(nsCString
* key
, PRBool
*collision
)
399 if (!Initialized()) return nsnull
; // NS_ERROR_NOT_INITIALIZED
400 nsDiskCacheRecord record
;
401 nsDiskCacheBinding
* binding
= nsnull
;
402 PLDHashNumber hashNumber
= nsDiskCache::Hash(key
->get());
404 *collision
= PR_FALSE
;
406 binding
= mBindery
.FindActiveBinding(hashNumber
);
407 if (binding
&& PL_strcmp(binding
->mCacheEntry
->Key()->get(), key
->get()) != 0) {
408 *collision
= PR_TRUE
;
413 // lookup hash number in cache map
414 nsresult rv
= mCacheMap
.FindRecord(hashNumber
, &record
);
415 if (NS_FAILED(rv
)) return nsnull
; // XXX log error?
417 nsDiskCacheEntry
* diskEntry
= mCacheMap
.ReadDiskCacheEntry(&record
);
418 if (!diskEntry
) return nsnull
;
420 // compare key to be sure
421 if (strcmp(diskEntry
->Key(), key
->get()) != 0) {
422 *collision
= PR_TRUE
;
426 nsCacheEntry
* entry
= diskEntry
->CreateCacheEntry(this);
427 if (!entry
) return nsnull
;
429 binding
= mBindery
.CreateBinding(entry
, &record
);
440 * NOTE: called while holding the cache service lock
443 nsDiskCacheDevice::DeactivateEntry(nsCacheEntry
* entry
)
446 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
447 NS_ASSERTION(binding
, "DeactivateEntry: binding == nsnull");
448 if (!binding
) return NS_ERROR_UNEXPECTED
;
450 CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n",
451 entry
, binding
->mRecord
.HashNumber()));
453 if (entry
->IsDoomed()) {
454 // delete data, entry, record from disk for entry
455 rv
= mCacheMap
.DeleteStorage(&binding
->mRecord
);
458 // save stuff to disk for entry
459 rv
= mCacheMap
.WriteDiskCacheEntry(binding
);
461 // clean up as best we can
462 (void) mCacheMap
.DeleteStorage(&binding
->mRecord
);
463 (void) mCacheMap
.DeleteRecord(&binding
->mRecord
);
464 binding
->mDoomed
= PR_TRUE
; // record is no longer in cache map
468 mBindery
.RemoveBinding(binding
); // extract binding from collision detection stuff
469 delete entry
; // which will release binding
476 * no hash number collision -> no problem
478 * record not active -> evict, no problem
480 * record is already doomed -> record shouldn't have been in map, no problem
481 * record is not doomed -> doom, and replace record in map
483 * walk matching hashnumber list to find lowest generation number
484 * take generation number from other (data/meta) location,
485 * or walk active list
487 * NOTE: called while holding the cache service lock
490 nsDiskCacheDevice::BindEntry(nsCacheEntry
* entry
)
492 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED
;
494 nsDiskCacheRecord record
, oldRecord
;
496 // create a new record for this entry
497 record
.SetHashNumber(nsDiskCache::Hash(entry
->Key()->get()));
498 record
.SetEvictionRank(ULONG_MAX
- SecondsFromPRTime(PR_Now()));
500 CACHE_LOG_DEBUG(("CACHE: disk BindEntry [%p %x]\n",
501 entry
, record
.HashNumber()));
503 if (!entry
->IsDoomed()) {
504 // if entry isn't doomed, add it to the cache map
505 rv
= mCacheMap
.AddRecord(&record
, &oldRecord
); // deletes old record, if any
506 if (NS_FAILED(rv
)) return rv
;
508 PRUint32 oldHashNumber
= oldRecord
.HashNumber();
510 // gotta evict this one first
511 nsDiskCacheBinding
* oldBinding
= mBindery
.FindActiveBinding(oldHashNumber
);
513 // XXX if debug : compare keys for hashNumber collision
515 if (!oldBinding
->mCacheEntry
->IsDoomed()) {
516 // we've got a live one!
517 nsCacheService::DoomEntry(oldBinding
->mCacheEntry
);
518 // storage will be delete when oldBinding->mCacheEntry is Deactivated
522 // XXX if debug : compare keys for hashNumber collision
523 rv
= mCacheMap
.DeleteStorage(&oldRecord
);
524 if (NS_FAILED(rv
)) return rv
; // XXX delete record we just added?
529 // Make sure this entry has its associated nsDiskCacheBinding attached.
530 nsDiskCacheBinding
* binding
= mBindery
.CreateBinding(entry
, &record
);
531 NS_ASSERTION(binding
, "nsDiskCacheDevice::BindEntry");
532 if (!binding
) return NS_ERROR_OUT_OF_MEMORY
;
533 NS_ASSERTION(binding
->mRecord
.ValidRecord(), "bad cache map record");
540 * NOTE: called while holding the cache service lock
543 nsDiskCacheDevice::DoomEntry(nsCacheEntry
* entry
)
545 CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry
));
547 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
548 NS_ASSERTION(binding
, "DoomEntry: binding == nsnull");
549 if (!binding
) return;
551 if (!binding
->mDoomed
) {
552 // so it can't be seen by FindEntry() ever again.
556 mCacheMap
.DeleteRecord(&binding
->mRecord
);
557 NS_ASSERTION(NS_SUCCEEDED(rv
),"DeleteRecord failed.");
558 binding
->mDoomed
= PR_TRUE
; // record in no longer in cache map
564 * NOTE: called while holding the cache service lock
567 nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry
* entry
,
568 nsCacheAccessMode mode
,
570 nsIInputStream
** result
)
572 CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n",
573 entry
, mode
, offset
));
575 NS_ENSURE_ARG_POINTER(entry
);
576 NS_ENSURE_ARG_POINTER(result
);
579 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
580 NS_ENSURE_TRUE(binding
, NS_ERROR_UNEXPECTED
);
582 NS_ASSERTION(binding
->mCacheEntry
== entry
, "binding & entry don't point to each other");
584 rv
= binding
->EnsureStreamIO();
585 if (NS_FAILED(rv
)) return rv
;
587 return binding
->mStreamIO
->GetInputStream(offset
, result
);
592 * NOTE: called while holding the cache service lock
595 nsDiskCacheDevice::OpenOutputStreamForEntry(nsCacheEntry
* entry
,
596 nsCacheAccessMode mode
,
598 nsIOutputStream
** result
)
600 CACHE_LOG_DEBUG(("CACHE: disk OpenOutputStreamForEntry [%p %x %u]\n",
601 entry
, mode
, offset
));
603 NS_ENSURE_ARG_POINTER(entry
);
604 NS_ENSURE_ARG_POINTER(result
);
607 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
608 NS_ENSURE_TRUE(binding
, NS_ERROR_UNEXPECTED
);
610 NS_ASSERTION(binding
->mCacheEntry
== entry
, "binding & entry don't point to each other");
612 rv
= binding
->EnsureStreamIO();
613 if (NS_FAILED(rv
)) return rv
;
615 return binding
->mStreamIO
->GetOutputStream(offset
, result
);
620 * NOTE: called while holding the cache service lock
623 nsDiskCacheDevice::GetFileForEntry(nsCacheEntry
* entry
,
626 NS_ENSURE_ARG_POINTER(result
);
631 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
633 NS_WARNING("GetFileForEntry: binding == nsnull");
634 return NS_ERROR_UNEXPECTED
;
637 // check/set binding->mRecord for separate file, sync w/mCacheMap
638 if (binding
->mRecord
.DataLocationInitialized()) {
639 if (binding
->mRecord
.DataFile() != 0)
640 return NS_ERROR_NOT_AVAILABLE
; // data not stored as separate file
642 NS_ASSERTION(binding
->mRecord
.DataFileGeneration() == binding
->mGeneration
, "error generations out of sync");
644 binding
->mRecord
.SetDataFileGeneration(binding
->mGeneration
);
645 binding
->mRecord
.SetDataFileSize(0); // 1k minimum
646 if (!binding
->mDoomed
) {
647 // record stored in cache map, so update it
648 rv
= mCacheMap
.UpdateRecord(&binding
->mRecord
);
649 if (NS_FAILED(rv
)) return rv
;
653 nsCOMPtr
<nsIFile
> file
;
654 rv
= mCacheMap
.GetFileForDiskCacheRecord(&binding
->mRecord
,
656 getter_AddRefs(file
));
657 if (NS_FAILED(rv
)) return rv
;
659 NS_IF_ADDREF(*result
= file
);
665 * This routine will get called every time an open descriptor is written to.
667 * NOTE: called while holding the cache service lock
670 nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry
* entry
, PRInt32 deltaSize
)
672 CACHE_LOG_DEBUG(("CACHE: disk OnDataSizeChange [%p %d]\n",
675 // If passed a negative value, then there's nothing to do.
679 nsDiskCacheBinding
* binding
= GetCacheEntryBinding(entry
);
680 NS_ASSERTION(binding
, "OnDataSizeChange: binding == nsnull");
681 if (!binding
) return NS_ERROR_UNEXPECTED
;
683 NS_ASSERTION(binding
->mRecord
.ValidRecord(), "bad record");
685 PRUint32 newSize
= entry
->DataSize() + deltaSize
;
686 PRUint32 newSizeK
= ((newSize
+ 0x3FF) >> 10);
688 // If the new size is larger than max. file size or larger than
689 // half the cache capacity (which is in KiB's), doom the entry and abort
690 if ((newSize
> kMaxDataFileSize
) || (newSizeK
> mCacheCapacity
/2)) {
694 nsCacheService::DoomEntry(entry
);
695 NS_ASSERTION(NS_SUCCEEDED(rv
),"DoomEntry() failed.");
696 return NS_ERROR_ABORT
;
699 PRUint32 sizeK
= ((entry
->DataSize() + 0x03FF) >> 10); // round up to next 1k
701 NS_ASSERTION(sizeK
< USHRT_MAX
, "data size out of range");
702 NS_ASSERTION(newSizeK
< USHRT_MAX
, "data size out of range");
704 // pre-evict entries to make space for new data
705 PRUint32 targetCapacity
= mCacheCapacity
> (newSizeK
- sizeK
)
706 ? mCacheCapacity
- (newSizeK
- sizeK
)
708 EvictDiskCacheEntries(targetCapacity
);
714 /******************************************************************************
716 *****************************************************************************/
717 class EntryInfoVisitor
: public nsDiskCacheRecordVisitor
720 EntryInfoVisitor(nsDiskCacheMap
* cacheMap
,
721 nsICacheVisitor
* visitor
)
722 : mCacheMap(cacheMap
)
726 virtual PRInt32
VisitRecord(nsDiskCacheRecord
* mapRecord
)
728 // XXX optimization: do we have this record in memory?
730 // read in the entry (metadata)
731 nsDiskCacheEntry
* diskEntry
= mCacheMap
->ReadDiskCacheEntry(mapRecord
);
733 return kVisitNextRecord
;
736 // create nsICacheEntryInfo
737 nsDiskCacheEntryInfo
* entryInfo
= new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID
, diskEntry
);
739 return kStopVisitingRecords
;
741 nsCOMPtr
<nsICacheEntryInfo
> ref(entryInfo
);
744 (void)mVisitor
->VisitEntry(DISK_CACHE_DEVICE_ID
, entryInfo
, &keepGoing
);
745 return keepGoing
? kVisitNextRecord
: kStopVisitingRecords
;
749 nsDiskCacheMap
* mCacheMap
;
750 nsICacheVisitor
* mVisitor
;
755 nsDiskCacheDevice::Visit(nsICacheVisitor
* visitor
)
757 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED
;
758 nsDiskCacheDeviceInfo
* deviceInfo
= new nsDiskCacheDeviceInfo(this);
759 nsCOMPtr
<nsICacheDeviceInfo
> ref(deviceInfo
);
762 nsresult rv
= visitor
->VisitDevice(DISK_CACHE_DEVICE_ID
, deviceInfo
, &keepGoing
);
763 if (NS_FAILED(rv
)) return rv
;
766 EntryInfoVisitor
infoVisitor(&mCacheMap
, visitor
);
767 return mCacheMap
.VisitRecords(&infoVisitor
);
775 nsDiskCacheDevice::EvictEntries(const char * clientID
)
777 CACHE_LOG_DEBUG(("CACHE: disk EvictEntries [%s]\n", clientID
));
779 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED
;
782 if (clientID
== nsnull
) {
783 // we're clearing the entire disk cache
784 rv
= ClearDiskCache();
785 if (rv
!= NS_ERROR_CACHE_IN_USE
)
789 nsDiskCacheEvictor
evictor(&mCacheMap
, &mBindery
, 0, clientID
);
790 rv
= mCacheMap
.VisitRecords(&evictor
);
792 if (clientID
== nsnull
) // we tried to clear the entire cache
793 rv
= mCacheMap
.Trim(); // so trim cache block files (if possible)
803 nsDiskCacheDevice::OpenDiskCache()
805 // if we don't have a cache directory, create one and open it
807 nsresult rv
= mCacheDirectory
->Exists(&exists
);
811 PRBool trashing
= PR_FALSE
;
813 // Try opening cache map file.
814 rv
= mCacheMap
.Open(mCacheDirectory
);
815 // move "corrupt" caches to trash
816 if (rv
== NS_ERROR_FILE_CORRUPTED
) {
817 rv
= DeleteDir(mCacheDirectory
, PR_TRUE
, PR_FALSE
);
823 else if (NS_FAILED(rv
))
827 // if we don't have a cache directory, create one and open it
829 rv
= mCacheDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
830 CACHE_LOG_PATH(PR_LOG_ALWAYS
, "\ncreate cache directory: %s\n", mCacheDirectory
);
831 CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv
));
835 // reopen the cache map
836 rv
= mCacheMap
.Open(mCacheDirectory
);
842 // delete any trash files leftover from a previous run
843 nsCOMPtr
<nsIFile
> trashDir
;
844 GetTrashDir(mCacheDirectory
, &trashDir
);
847 if (NS_SUCCEEDED(trashDir
->Exists(&exists
)) && exists
)
848 DeleteDir(trashDir
, PR_FALSE
, PR_FALSE
);
857 nsDiskCacheDevice::ClearDiskCache()
859 if (mBindery
.ActiveBindings())
860 return NS_ERROR_CACHE_IN_USE
;
862 nsresult rv
= Shutdown_Private(PR_FALSE
); // false: don't bother flushing
866 // If the disk cache directory is already gone, then it's not an error if
867 // we fail to delete it ;-)
868 rv
= DeleteDir(mCacheDirectory
, PR_TRUE
, PR_FALSE
);
869 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
)
877 nsDiskCacheDevice::EvictDiskCacheEntries(PRUint32 targetCapacity
)
879 CACHE_LOG_DEBUG(("CACHE: disk EvictDiskCacheEntries [%u]\n",
882 NS_ASSERTION(targetCapacity
> 0, "oops");
884 if (mCacheMap
.TotalSize() < targetCapacity
)
887 // targetCapacity is in KiB's
888 nsDiskCacheEvictor
evictor(&mCacheMap
, &mBindery
, targetCapacity
, nsnull
);
889 return mCacheMap
.EvictRecords(&evictor
);
898 nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile
* parentDir
)
904 NS_ASSERTION(PR_FALSE
, "Cannot switch cache directory when initialized");
909 mCacheDirectory
= nsnull
;
913 // ensure parent directory exists
914 rv
= parentDir
->Exists(&exists
);
915 if (NS_SUCCEEDED(rv
) && !exists
)
916 rv
= parentDir
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
917 if (NS_FAILED(rv
)) return;
919 // ensure cache directory exists
920 nsCOMPtr
<nsIFile
> directory
;
922 rv
= parentDir
->Clone(getter_AddRefs(directory
));
923 if (NS_FAILED(rv
)) return;
924 rv
= directory
->AppendNative(NS_LITERAL_CSTRING("Cache"));
925 if (NS_FAILED(rv
)) return;
927 mCacheDirectory
= do_QueryInterface(directory
);
932 nsDiskCacheDevice::getCacheDirectory(nsILocalFile
** result
)
934 *result
= mCacheDirectory
;
935 NS_IF_ADDREF(*result
);
940 * NOTE: called while holding the cache service lock
943 nsDiskCacheDevice::SetCapacity(PRUint32 capacity
)
946 mCacheCapacity
= capacity
;
948 // start evicting entries if the new size is smaller!
949 EvictDiskCacheEntries(mCacheCapacity
);
954 PRUint32
nsDiskCacheDevice::getCacheCapacity()
956 return mCacheCapacity
;
960 PRUint32
nsDiskCacheDevice::getCacheSize()
962 return mCacheMap
.TotalSize();
966 PRUint32
nsDiskCacheDevice::getEntryCount()
968 return mCacheMap
.EntryCount();