зеркало из https://github.com/mozilla/gecko-dev.git
751 строка
18 KiB
C++
751 строка
18 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator.
|
|
*
|
|
* The Initial Developer of the Original Code is Intel Corp.
|
|
* Portions created by Intel Corp. are
|
|
* Copyright (C) 1999, 1999 Intel Corp. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
|
|
* Carl Wong <carl.wong@intel.com>
|
|
*/
|
|
|
|
#include "nsNetDiskCache.h"
|
|
#include "nscore.h"
|
|
|
|
#include "plstr.h"
|
|
#include "prprf.h"
|
|
#include "prtypes.h"
|
|
#include "prio.h"
|
|
#include "prsystem.h" // Directory Seperator
|
|
#include "plhash.h"
|
|
#include "prclist.h"
|
|
#include "prmem.h"
|
|
#include "prlog.h"
|
|
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIPref.h"
|
|
#include "mcom_db.h"
|
|
#include "nsDBEnumerator.h"
|
|
|
|
#include "nsDiskCacheRecord.h"
|
|
#include "netCore.h"
|
|
#include "nsFileLocations.h"
|
|
#include "nsIFileLocator.h"
|
|
#include "nsIFile.h"
|
|
#include "nsILocalFile.h"
|
|
#include "nsIFileSpec.h" // Remove Me later
|
|
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
|
|
ERROR! Must have a byte order
|
|
#endif
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
|
|
#else
|
|
#define COPY_INT32(_a,_b) /* swap */ \
|
|
do { \
|
|
((char *)(_a))[0] = ((char *)(_b))[3]; \
|
|
((char *)(_a))[1] = ((char *)(_b))[2]; \
|
|
((char *)(_a))[2] = ((char *)(_b))[1]; \
|
|
((char *)(_a))[3] = ((char *)(_b))[0]; \
|
|
} while(0)
|
|
#endif
|
|
|
|
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID) ;
|
|
static NS_DEFINE_CID(kDBAccessorCID, NS_DBACCESSOR_CID) ;
|
|
|
|
static const PRUint32 DISK_CACHE_SIZE_DEFAULT = 5*1024*1024 ; // 5MB
|
|
static const char * const DISK_CACHE_SIZE_PREF = "browser.cache.disk_cache_size";
|
|
static const char * const CACHE_DIR_PREF = "browser.cache.directory";
|
|
static const char * const CACHE_ENABLE_PREF = "browser.cache.disk.enable";
|
|
|
|
|
|
static int folderChanged(const char *pref, void *closure)
|
|
{
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
|
|
if ( NS_FAILED (rv ) )
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIFileSpec> folder;
|
|
rv = prefs->GetFilePref(CACHE_DIR_PREF, getter_AddRefs( folder ) );
|
|
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
// This is really wrong and should be fixed when the pref code is update
|
|
// If someone has a system where path names are not unique they are going to
|
|
// be in a world of hurt.
|
|
|
|
nsCOMPtr<nsILocalFile> cacheFolder;
|
|
char* path = NULL;
|
|
rv = folder->GetNativePath(& path );
|
|
if ( NS_FAILED ( rv ) )
|
|
return rv;
|
|
|
|
rv = NS_NewLocalFile( path, getter_AddRefs(cacheFolder));
|
|
nsAllocator::Free( path );
|
|
|
|
return ( (nsNetDiskCache*)closure )->SetDiskCacheFolder( cacheFolder );
|
|
}
|
|
|
|
static int enableChanged(const char *pref, void *closure)
|
|
{
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
|
|
if ( NS_FAILED (rv ) )
|
|
return rv;
|
|
|
|
PRBool enabled;
|
|
rv = prefs->GetBoolPref(CACHE_ENABLE_PREF, &enabled );
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
return ( (nsNetDiskCache*)closure )->SetEnabled( enabled );
|
|
}
|
|
|
|
class nsDiskCacheRecord ;
|
|
|
|
nsNetDiskCache::nsNetDiskCache() :
|
|
mEnabled(PR_FALSE) ,
|
|
mNumEntries(0) ,
|
|
mpNextCache(0) ,
|
|
mDiskCacheFolder(0) ,
|
|
mStorageInUse(0) ,
|
|
mDB(0) ,
|
|
mDBCorrupted(PR_FALSE)
|
|
{
|
|
// set it to INF for now
|
|
mMaxEntries = (PRUint32)-1 ;
|
|
|
|
NS_INIT_REFCNT();
|
|
|
|
}
|
|
|
|
nsNetDiskCache::~nsNetDiskCache()
|
|
{
|
|
if ( mDB )
|
|
SetSizeEntry();
|
|
|
|
NS_IF_RELEASE(mDB) ;
|
|
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
|
|
if ( NS_SUCCEEDED (rv ) )
|
|
{
|
|
prefs->UnregisterCallback( CACHE_DIR_PREF, folderChanged, this );
|
|
prefs->UnregisterCallback( CACHE_ENABLE_PREF, enableChanged, this );
|
|
}
|
|
// FUR
|
|
// I think that, eventually, we also want a distinguished key in the DB which
|
|
// means "clean cache shutdown". You clear this flag when the db is first
|
|
// opened and set it just before the db is closed. If the db wasn't shutdown
|
|
// cleanly in a prior session, i.e. because the app crashed, on startup you
|
|
// scan all the individual files in directories and look for "orphans",
|
|
// i.e. cache files which don't have corresponding entries in the db. That's
|
|
// also when storage-in-use and number of entries would be recomputed.
|
|
//
|
|
// We don't necessarily need all this functionality immediately, though.
|
|
|
|
|
|
if(mDBCorrupted) {
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
|
|
nsresult res = mDiskCacheFolder->GetDirectoryEntries( getter_AddRefs( directoryEnumerator) ) ;
|
|
if ( NS_FAILED ( res ) )
|
|
return;
|
|
|
|
nsCString trash("trash") ;
|
|
nsCOMPtr<nsIFile> file;
|
|
|
|
PRBool hasMore;
|
|
while( NS_SUCCEEDED(directoryEnumerator->HasMoreElements( &hasMore ) ) && hasMore)
|
|
{
|
|
nsresult rv = directoryEnumerator->GetNext( getter_AddRefs(file) );
|
|
if ( NS_FAILED( rv ) )
|
|
return ;
|
|
|
|
char* filename;
|
|
rv = file->GetLeafName( &filename );
|
|
if ( NS_FAILED( rv ) )
|
|
return;
|
|
|
|
|
|
if( trash.CompareWithConversion( filename, PR_FALSE, 5 ) == 0)
|
|
file->Delete( PR_TRUE );
|
|
|
|
nsCRT::free(filename) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP nsNetDiskCache::InitCacheFolder()
|
|
{
|
|
|
|
// don't initialize if no cache folder is set.
|
|
if(!mDiskCacheFolder) return NS_OK ;
|
|
|
|
nsresult rv;
|
|
if(!mDB) {
|
|
mDB = new nsDBAccessor() ;
|
|
if(!mDB)
|
|
return NS_ERROR_OUT_OF_MEMORY ;
|
|
else
|
|
NS_ADDREF(mDB) ;
|
|
}
|
|
|
|
|
|
rv = InitDB();
|
|
if ( NS_FAILED( rv ) )
|
|
rv = DBRecovery();
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
// create cache sub directories
|
|
// Do this after initializing the database to avoid the situtation where on the first initialization
|
|
// YOu create the folders and then immediately mark them for deletion.
|
|
nsCOMPtr<nsIFile> cacheSubDir;
|
|
|
|
for (int i=0; i < 32; i++) {
|
|
rv = mDiskCacheFolder->Clone( getter_AddRefs( cacheSubDir ) );
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
char dirName[3];
|
|
PR_snprintf (dirName, 3, "%0.2x", i);
|
|
cacheSubDir->Append (dirName) ;
|
|
CreateDir(cacheSubDir);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::Init(void)
|
|
{
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
|
|
if ( NS_FAILED (rv ) )
|
|
return rv;
|
|
rv = prefs->RegisterCallback( CACHE_DIR_PREF, folderChanged, this);
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
rv = prefs->RegisterCallback( CACHE_ENABLE_PREF, enableChanged, this);
|
|
if ( NS_FAILED( rv ) )
|
|
return rv;
|
|
|
|
rv = folderChanged(CACHE_DIR_PREF , this );
|
|
enableChanged(CACHE_ENABLE_PREF , this );
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::InitDB(void)
|
|
{
|
|
nsresult rv ;
|
|
|
|
rv =mDiskCacheFolder->Clone( getter_AddRefs( mDBFile ) );
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
mDBFile->Append("cache.db") ;
|
|
#ifdef XP_MAC
|
|
PRBool exists;
|
|
if ( NS_SUCCEEDED( mDBFile->Exists(&exists ) ) && !exists)
|
|
{
|
|
mDBFile->Create( nsIFile::NORMAL_FILE_TYPE, PR_IRUSR | PR_IWUSR );
|
|
}
|
|
#endif
|
|
rv = mDB->Init(mDBFile) ;
|
|
if ( NS_SUCCEEDED( rv ) )
|
|
rv = GetSizeEntry();
|
|
return rv ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// nsISupports methods
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS3(nsNetDiskCache,
|
|
nsINetDataDiskCache,
|
|
nsINetDataCache,
|
|
nsISupports)
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// nsINetDataCache Method
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetDescription(PRUnichar* *aDescription)
|
|
{
|
|
nsAutoString description ;
|
|
description.AssignWithConversion("Disk Cache") ;
|
|
|
|
*aDescription = description.ToNewUnicode() ;
|
|
if(!*aDescription)
|
|
return NS_ERROR_OUT_OF_MEMORY ;
|
|
|
|
return NS_OK ;
|
|
}
|
|
|
|
/* don't alloc mem for nsICachedNetData.
|
|
* RecordID is generated using the same scheme in nsCacheDiskData,
|
|
* see GetCachedNetData() for detail.
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::Contains(const char* key, PRUint32 length, PRBool *_retval)
|
|
{
|
|
*_retval = PR_FALSE ;
|
|
|
|
NS_ASSERTION(mDB, "no db.") ;
|
|
|
|
PRInt32 id = 0 ;
|
|
nsresult rv = mDB->GetID(key, length, &id) ;
|
|
|
|
if(NS_FAILED(rv)) {
|
|
// try recovery if error
|
|
DBRecovery() ;
|
|
return rv ;
|
|
}
|
|
|
|
void* info = 0 ;
|
|
PRUint32 info_size = 0 ;
|
|
|
|
rv = mDB->Get(id, &info, &info_size) ;
|
|
if(NS_SUCCEEDED(rv) && info)
|
|
*_retval = PR_TRUE ;
|
|
|
|
if(NS_FAILED(rv)) {
|
|
// try recovery if error
|
|
DBRecovery() ;
|
|
}
|
|
|
|
return rv ;
|
|
}
|
|
|
|
/* regardless if it's cached or not, a copy of nsNetDiskCache would
|
|
* always be returned. so release it appropriately.
|
|
* if mem allocated, update mNumEntries also.
|
|
* for now, the new nsCachedNetData is not written into db yet since
|
|
* we have nothing to write.
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetCachedNetData(const char* key, PRUint32 length, nsINetDataCacheRecord **_retval)
|
|
{
|
|
NS_ASSERTION(mDB, "no db.");
|
|
|
|
nsresult rv = 0;
|
|
if (!_retval)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*_retval = nsnull;
|
|
|
|
PRInt32 id = 0;
|
|
rv = mDB->GetID(key, length, &id);
|
|
|
|
if ( NS_SUCCEEDED ( rv )) {
|
|
// construct an empty record
|
|
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(mDB, this);
|
|
if (!newRecord)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
rv = newRecord->Init(key, length, id);
|
|
if ( NS_FAILED( rv) ) {
|
|
delete newRecord;
|
|
return rv;
|
|
}
|
|
|
|
NS_ADDREF(newRecord); // addref for _retval
|
|
*_retval = (nsINetDataCacheRecord*) newRecord;
|
|
|
|
void* info = 0;
|
|
PRUint32 info_size = 0;
|
|
|
|
rv = mDB->Get(id, &info, &info_size);
|
|
if ( NS_SUCCEEDED( rv ) ) {
|
|
if ( info ) {
|
|
// this is a previously cached record
|
|
rv = newRecord->RetrieveInfo(info, info_size);
|
|
if ( NS_FAILED( rv ) ) {
|
|
// probably a bad one
|
|
NS_RELEASE(newRecord);
|
|
*_retval = nsnull;
|
|
return rv;
|
|
}
|
|
} else {
|
|
// this is a new record.
|
|
mNumEntries ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NS_FAILED( rv ) )
|
|
DBRecovery();
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetCachedNetDataByID(PRInt32 RecordID, nsINetDataCacheRecord **_retval)
|
|
{
|
|
NS_ASSERTION(mDB, "no db.");
|
|
|
|
nsresult rv;
|
|
void* info = 0;
|
|
PRUint32 info_size = 0;
|
|
|
|
if (!_retval)
|
|
return NS_ERROR_NULL_POINTER;
|
|
*_retval = nsnull;
|
|
|
|
rv = mDB->Get(RecordID, &info, &info_size);
|
|
if(NS_SUCCEEDED(rv) && info) {
|
|
// construct an empty record if only found in db
|
|
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(mDB, this);
|
|
if(!newRecord)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
rv = newRecord->RetrieveInfo(info, info_size);
|
|
if(NS_SUCCEEDED(rv)) {
|
|
*_retval = (nsINetDataCacheRecord*) newRecord;
|
|
NS_ADDREF(*_retval);
|
|
} else {
|
|
delete newRecord;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_WARNING("Error: RecordID not in DB\n");
|
|
DBRecovery();
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetEnabled(PRBool *aEnabled)
|
|
{
|
|
*aEnabled = mEnabled ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::SetEnabled(PRBool aEnabled)
|
|
{
|
|
mEnabled = aEnabled ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetFlags(PRUint32 *aFlags)
|
|
{
|
|
*aFlags = FILE_PER_URL_CACHE;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetNumEntries(PRUint32 *aNumEntries)
|
|
{
|
|
*aNumEntries = mNumEntries ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetMaxEntries(PRUint32 *aMaxEntries)
|
|
{
|
|
*aMaxEntries = mMaxEntries ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::NewCacheEntryIterator(nsISimpleEnumerator **_retval)
|
|
{
|
|
NS_ASSERTION(mDB, "no db.") ;
|
|
|
|
if(!_retval)
|
|
return NS_ERROR_NULL_POINTER ;
|
|
|
|
*_retval = nsnull ;
|
|
|
|
nsDBEnumerator* enumerator = new nsDBEnumerator(mDB, this) ;
|
|
if(enumerator)
|
|
return enumerator->QueryInterface( nsISimpleEnumerator::GetIID(), (void**)_retval );
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetNextCache(nsINetDataCache * *aNextCache)
|
|
{
|
|
if(!aNextCache)
|
|
return NS_ERROR_NULL_POINTER ;
|
|
|
|
*aNextCache = mpNextCache ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::SetNextCache(nsINetDataCache *aNextCache)
|
|
{
|
|
mpNextCache = aNextCache ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
// db size can always be measured at the last minute. Since it's hard
|
|
// to know before hand.
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetStorageInUse(PRUint32 *aStorageInUse)
|
|
{
|
|
NS_ASSERTION(mDB, "no db.");
|
|
|
|
PRUint32 total_size = mStorageInUse;
|
|
|
|
// we need size in kB
|
|
total_size = total_size >> 10 ;
|
|
|
|
*aStorageInUse = total_size ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
/*
|
|
* The whole cache dirs can be whiped clean since all the cache
|
|
* files are resides in seperate hashed dirs. It's safe to do so.
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::RemoveAll(void)
|
|
{
|
|
NS_ASSERTION(mDB, "no db.") ;
|
|
NS_ASSERTION(mDiskCacheFolder, "no cache folder.") ;
|
|
|
|
|
|
|
|
// Shutdown the database
|
|
mDB->Shutdown() ;
|
|
|
|
// Delete all the files in the cache directory
|
|
// Could I just delete the cache folder????? --DJM
|
|
nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
|
|
nsresult res = mDiskCacheFolder->GetDirectoryEntries( getter_AddRefs( directoryEnumerator) ) ;
|
|
if ( NS_FAILED ( res ) )
|
|
return res;
|
|
|
|
nsCOMPtr<nsIFile> file;
|
|
|
|
PRBool hasMore;
|
|
while( NS_SUCCEEDED(directoryEnumerator->HasMoreElements( &hasMore ) ) && hasMore)
|
|
{
|
|
nsresult rv = directoryEnumerator->GetNext( getter_AddRefs(file) );
|
|
if ( NS_FAILED( rv ) )
|
|
return rv ;
|
|
PRBool isDirectory;
|
|
if ( NS_SUCCEEDED(file->IsDirectory( &isDirectory )) && isDirectory )
|
|
file->Delete( PR_TRUE );
|
|
|
|
}
|
|
if ( mDBFile )
|
|
mDBFile->Delete(PR_FALSE) ;
|
|
// reinitilize
|
|
return InitCacheFolder() ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// nsINetDataDiskCache methods
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetDiskCacheFolder(nsIFile * *aDiskCacheFolder)
|
|
{
|
|
*aDiskCacheFolder = nsnull ;
|
|
NS_ASSERTION(mDiskCacheFolder, "no cache folder.") ;
|
|
|
|
*aDiskCacheFolder = mDiskCacheFolder ;
|
|
NS_ADDREF(*aDiskCacheFolder) ;
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::SetDiskCacheFolder(nsIFile * aDiskCacheFolder)
|
|
{
|
|
if(!mDiskCacheFolder){
|
|
mDiskCacheFolder = aDiskCacheFolder ;
|
|
return InitCacheFolder() ;
|
|
}
|
|
else {
|
|
PRBool result;
|
|
if( NS_SUCCEEDED( mDiskCacheFolder->Equals( aDiskCacheFolder, &result ) ) && result ) {
|
|
mDiskCacheFolder = aDiskCacheFolder ;
|
|
|
|
// do we need to blow away old cache before building a new one?
|
|
// mDiskFolder points to the old cache so you can't call RemoveALL
|
|
// need to refactor.
|
|
// return RemoveAll() ;
|
|
|
|
mDB->Shutdown() ;
|
|
return InitCacheFolder() ;
|
|
|
|
} else
|
|
return NS_OK ;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// nsNetDiskCache methods
|
|
|
|
// create a directory (recursively)
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::CreateDir(nsIFile* dir_spec)
|
|
{
|
|
PRBool does_exist ;
|
|
nsCOMPtr<nsIFile> p_spec ;
|
|
|
|
dir_spec->Exists(&does_exist) ;
|
|
if(does_exist)
|
|
return NS_OK ;
|
|
|
|
nsresult rv = dir_spec->GetParent(getter_AddRefs(p_spec)) ;
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
p_spec->Exists(&does_exist) ;
|
|
if(!does_exist) {
|
|
rv = CreateDir(p_spec) ;
|
|
if ( NS_FAILED( rv ))
|
|
return rv;
|
|
}
|
|
|
|
rv = dir_spec->Create( nsIFile::DIRECTORY_TYPE, PR_IRWXU) ;
|
|
return rv ;
|
|
}
|
|
|
|
// We can't afford to make a *separate* pass over the whole db on every
|
|
// startup, just to figure out mNumEntries and mStorageInUse. (This is a
|
|
// several second operation on a large db). We'll likely need to store
|
|
// distinguished keys in the db that contain these values and update them
|
|
// incrementally, except when failure to shut down the db cleanly is detected.
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::GetSizeEntry(void)
|
|
{
|
|
void* pInfo ;
|
|
PRUint32 InfoSize ;
|
|
|
|
nsresult rv = mDB->GetSizeEntry(&pInfo, &InfoSize) ;
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
if(!pInfo && InfoSize == 0) {
|
|
// must be a new DB
|
|
mNumEntries = 0 ;
|
|
mStorageInUse = 0 ;
|
|
}
|
|
else {
|
|
char * cur_ptr = NS_STATIC_CAST(char*, pInfo) ;
|
|
|
|
// get mNumEntries
|
|
COPY_INT32(&mNumEntries, cur_ptr) ;
|
|
cur_ptr += sizeof(PRUint32) ;
|
|
|
|
// get mStorageInUse
|
|
COPY_INT32(&mStorageInUse, cur_ptr) ;
|
|
cur_ptr += sizeof(PRUint32) ;
|
|
|
|
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, pInfo) + InfoSize);
|
|
}
|
|
|
|
return NS_OK ;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::SetSizeEntry(void)
|
|
{
|
|
PRUint32 InfoSize ;
|
|
|
|
InfoSize = sizeof mNumEntries ;
|
|
InfoSize += sizeof mStorageInUse ;
|
|
|
|
void* pInfo = nsAllocator::Alloc(InfoSize*sizeof(char)) ;
|
|
if(!pInfo)
|
|
return NS_ERROR_OUT_OF_MEMORY ;
|
|
|
|
char* cur_ptr = NS_STATIC_CAST(char*, pInfo) ;
|
|
|
|
COPY_INT32(cur_ptr, &mNumEntries) ;
|
|
cur_ptr += sizeof(PRUint32) ;
|
|
|
|
COPY_INT32(cur_ptr, &mStorageInUse) ;
|
|
cur_ptr += sizeof(PRUint32) ;
|
|
|
|
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, pInfo) + InfoSize);
|
|
|
|
return mDB->SetSizeEntry(pInfo, InfoSize) ;
|
|
}
|
|
|
|
// this routine will be called everytime we have a db corruption.
|
|
// mDB will be re-initialized, mStorageInUse and mNumEntries will
|
|
// be reset.
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::DBRecovery(void)
|
|
{
|
|
// rename all the sub cache dirs and remove them later during dtor.
|
|
nsresult rv = RenameCacheSubDirs() ;
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
// remove corrupted db file, don't care if db->shutdown fails or not.
|
|
mDB->Shutdown() ;
|
|
|
|
|
|
// False since we want to delete a file rather than recursively delete a directory
|
|
rv = mDBFile->Delete(PR_FALSE) ;
|
|
if (NS_FAILED ( rv ) ) return rv;
|
|
|
|
|
|
// reinitilize DB
|
|
return InitDB() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this routine will add string "trash" to current CacheSubDir names.
|
|
// e.g. 00->trash00, 1f->trash1f. and update the mDBCorrupted.
|
|
|
|
NS_IMETHODIMP
|
|
nsNetDiskCache::RenameCacheSubDirs(void)
|
|
{
|
|
nsCOMPtr<nsIFile> cacheSubDir;
|
|
nsresult rv;
|
|
|
|
for (int i=0; i < 32; i++) {
|
|
rv = mDiskCacheFolder->Clone( getter_AddRefs( cacheSubDir ) );
|
|
if(NS_FAILED(rv))
|
|
return rv ;
|
|
|
|
char oldName[3], newName[8];
|
|
PR_snprintf(oldName, 3, "%0.2x", i) ;
|
|
cacheSubDir->Append(oldName) ;
|
|
|
|
// re-name the directory
|
|
PR_snprintf(newName, 8, "trash%0.2x", i) ;
|
|
rv = cacheSubDir->MoveTo( mDiskCacheFolder, newName );
|
|
if(NS_FAILED(rv))
|
|
// TODO, error checking
|
|
return NS_ERROR_FAILURE ;
|
|
}
|
|
|
|
// update mDBCorrupted
|
|
mDBCorrupted = PR_TRUE ;
|
|
|
|
return NS_OK ;
|
|
}
|