mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-26 19:55:39 +00:00
Bug 727370 - Make SafeBrowsing updates atomic transactions. r=dcamp
This commit is contained in:
parent
05afeb75f6
commit
7530b9f562
@ -1,41 +1,7 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Classifier.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
@ -59,6 +25,10 @@ extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
#define STORE_DIRECTORY NS_LITERAL_CSTRING("safebrowsing")
|
||||
#define TO_DELETE_DIR_SUFFIX NS_LITERAL_CSTRING("-to_delete")
|
||||
#define BACKUP_DIR_SUFFIX NS_LITERAL_CSTRING("-backup")
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
@ -144,21 +114,62 @@ Classifier::InitKey()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::SetupPathNames()
|
||||
{
|
||||
// Get the root directory where to store all the databases.
|
||||
nsresult rv = mCacheDirectory->Clone(getter_AddRefs(mStoreDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->AppendNative(STORE_DIRECTORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure LookupCaches (which are persistent and survive updates)
|
||||
// are reading/writing in the right place. We will be moving their
|
||||
// files "underneath" them during backup/restore.
|
||||
for (uint32 i = 0; i < mLookupCaches.Length(); i++) {
|
||||
mLookupCaches[i]->UpdateDirHandle(mStoreDirectory);
|
||||
}
|
||||
|
||||
// Directory where to move a backup before an update.
|
||||
rv = mCacheDirectory->Clone(getter_AddRefs(mBackupDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mBackupDirectory->AppendNative(STORE_DIRECTORY + BACKUP_DIR_SUFFIX);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Directory where to move the backup so we can atomically
|
||||
// delete (really move) it.
|
||||
rv = mCacheDirectory->Clone(getter_AddRefs(mToDeleteDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mToDeleteDirectory->AppendNative(STORE_DIRECTORY + TO_DELETE_DIR_SUFFIX);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Open(nsIFile& aCacheDirectory)
|
||||
{
|
||||
nsresult rv;
|
||||
// Remember the Local profile directory.
|
||||
nsresult rv = aCacheDirectory.Clone(getter_AddRefs(mCacheDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
// Create the handles to the update and backup directories.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Clean up any to-delete directories that haven't been deleted yet.
|
||||
rv = CleanToDelete();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check whether we have an incomplete update and recover from the
|
||||
// backup if so.
|
||||
rv = RecoverBackups();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Ensure the safebrowsing directory exists.
|
||||
rv = aCacheDirectory.Clone(getter_AddRefs(mStoreDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->AppendNative(NS_LITERAL_CSTRING("safebrowsing"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool storeExists;
|
||||
rv = mStoreDirectory->Exists(&storeExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -174,6 +185,9 @@ Classifier::Open(nsIFile& aCacheDirectory)
|
||||
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
||||
}
|
||||
|
||||
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = InitKey();
|
||||
if (NS_FAILED(rv)) {
|
||||
// Without a usable key the database is useless
|
||||
@ -182,44 +196,31 @@ Classifier::Open(nsIFile& aCacheDirectory)
|
||||
}
|
||||
|
||||
mTableFreshness.Init();
|
||||
|
||||
// Build the list of know urlclassifier lists
|
||||
// XXX: Disk IO potentially on the main thread during startup
|
||||
RegenActiveTables();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
Classifier::Close()
|
||||
{
|
||||
DropStores();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
Classifier::Reset()
|
||||
{
|
||||
DropStores();
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = entries->GetNext(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = file->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mStoreDirectory->Remove(true);
|
||||
mBackupDirectory->Remove(true);
|
||||
mToDeleteDirectory->Remove(true);
|
||||
|
||||
mTableFreshness.Clear();
|
||||
RegenActiveTables();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -356,15 +357,19 @@ Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG(("Applying table updates."));
|
||||
LOG(("Backup before update."));
|
||||
|
||||
nsresult rv;
|
||||
nsresult rv = BackupTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Applying table updates."));
|
||||
|
||||
for (uint32 i = 0; i < aUpdates->Length(); i++) {
|
||||
// Previous ApplyTableUpdates() may have consumed this update..
|
||||
if ((*aUpdates)[i]) {
|
||||
// Run all updates for one table
|
||||
rv = ApplyTableUpdates(aUpdates, aUpdates->ElementAt(i)->TableName());
|
||||
nsCString updateTable(aUpdates->ElementAt(i)->TableName());
|
||||
rv = ApplyTableUpdates(aUpdates, updateTable);
|
||||
if (NS_FAILED(rv)) {
|
||||
Reset();
|
||||
return rv;
|
||||
@ -372,7 +377,21 @@ Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
|
||||
}
|
||||
}
|
||||
aUpdates->Clear();
|
||||
RegenActiveTables();
|
||||
|
||||
rv = RegenActiveTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Cleaning up backups."));
|
||||
|
||||
// Move the backup directory away (signaling the transaction finished
|
||||
// successfully). This is atomic.
|
||||
rv = RemoveBackupTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Do the actual deletion of the backup files.
|
||||
rv = CleanToDelete();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Done applying updates."));
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
@ -432,6 +451,9 @@ Classifier::RegenActiveTables()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lookupCache->IsPrimed())
|
||||
continue;
|
||||
|
||||
const ChunkSet &adds = store->AddChunks();
|
||||
const ChunkSet &subs = store->SubChunks();
|
||||
|
||||
@ -482,6 +504,102 @@ Classifier::ActiveTables(nsTArray<nsCString>& aTables)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::CleanToDelete()
|
||||
{
|
||||
bool exists;
|
||||
nsresult rv = mToDeleteDirectory->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
rv = mToDeleteDirectory->Remove(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::BackupTables()
|
||||
{
|
||||
// We have to work in reverse here: first move the normal directory
|
||||
// away to be the backup directory, then copy the files over
|
||||
// to the normal directory. This ensures that if we crash the backup
|
||||
// dir always has a valid, complete copy, instead of a partial one,
|
||||
// because that's the one we will copy over the normal store dir.
|
||||
|
||||
nsCString backupDirName;
|
||||
nsresult rv = mBackupDirectory->GetNativeLeafName(backupDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString storeDirName;
|
||||
rv = mStoreDirectory->GetNativeLeafName(storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->MoveToNative(nullptr, backupDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->CopyToNative(nullptr, storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We moved some things to new places, so move the handles around, too.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::RemoveBackupTables()
|
||||
{
|
||||
nsCString toDeleteName;
|
||||
nsresult rv = mToDeleteDirectory->GetNativeLeafName(toDeleteName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mBackupDirectory->MoveToNative(nullptr, toDeleteName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mBackupDirectory now points to toDelete, fix that up.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::RecoverBackups()
|
||||
{
|
||||
bool backupExists;
|
||||
nsresult rv = mBackupDirectory->Exists(&backupExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (backupExists) {
|
||||
// Remove the safebrowsing dir if it exists
|
||||
nsCString storeDirName;
|
||||
rv = mStoreDirectory->GetNativeLeafName(storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool storeExists;
|
||||
rv = mStoreDirectory->Exists(&storeExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (storeExists) {
|
||||
rv = mStoreDirectory->Remove(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Move the backup to the store location
|
||||
rv = mBackupDirectory->MoveToNative(nullptr, storeDirName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mBackupDirectory now points to storeDir, fix up.
|
||||
rv = SetupPathNames();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will consume+delete updates from the passed nsTArray.
|
||||
*/
|
||||
@ -592,10 +710,6 @@ Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
||||
rv = prefixSet->WriteFile();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// This will drop all the temporary storage used during the update.
|
||||
rv = store->FinishUpdate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (updateFreshness) {
|
||||
int64_t now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
LOG(("Successfully updated %s", store->TableName().get()));
|
||||
|
@ -28,8 +28,8 @@ public:
|
||||
~Classifier();
|
||||
|
||||
nsresult Open(nsIFile& aCacheDirectory);
|
||||
nsresult Close();
|
||||
nsresult Reset();
|
||||
void Close();
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Get the list of active tables and their chunks in a format
|
||||
@ -71,6 +71,11 @@ public:
|
||||
PrefixArray* aNoiseEntries);
|
||||
private:
|
||||
void DropStores();
|
||||
nsresult SetupPathNames();
|
||||
nsresult RecoverBackups();
|
||||
nsresult CleanToDelete();
|
||||
nsresult BackupTables();
|
||||
nsresult RemoveBackupTables();
|
||||
nsresult RegenActiveTables();
|
||||
nsresult ScanStoreDir(nsTArray<nsCString>& aTables);
|
||||
|
||||
@ -80,8 +85,14 @@ private:
|
||||
LookupCache *GetLookupCache(const nsACString& aTable);
|
||||
nsresult InitKey();
|
||||
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
// Root dir of the Local profile.
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
// Main directory where to store the databases.
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
// Used for atomically updating the other dirs.
|
||||
nsCOMPtr<nsIFile> mBackupDirectory;
|
||||
nsCOMPtr<nsIFile> mToDeleteDirectory;
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
nsTArray<HashStore*> mHashStores;
|
||||
nsTArray<LookupCache*> mLookupCaches;
|
||||
nsTArray<nsCString> mActiveTablesCache;
|
||||
|
@ -166,8 +166,6 @@ HashStore::Reset()
|
||||
rv = storeFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -230,7 +228,6 @@ HashStore::Open()
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -271,24 +268,9 @@ HashStore::Open()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HashStore::Clear()
|
||||
{
|
||||
mAddChunks.Clear();
|
||||
mSubChunks.Clear();
|
||||
mAddExpirations.Clear();
|
||||
mSubExpirations.Clear();
|
||||
mAddPrefixes.Clear();
|
||||
mSubPrefixes.Clear();
|
||||
mAddCompletes.Clear();
|
||||
mSubCompletes.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadEntireStore()
|
||||
{
|
||||
Clear();
|
||||
|
||||
nsresult rv = ReadHeader();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -308,7 +290,6 @@ nsresult
|
||||
HashStore::ReadHeader()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -409,8 +390,6 @@ nsresult
|
||||
HashStore::ReadChunkNumbers()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
LOG(("Clearing."));
|
||||
Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -465,6 +444,11 @@ HashStore::BeginUpdate()
|
||||
nsresult rv = ReadEntireStore();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mInputStream) {
|
||||
rv = mInputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -835,13 +819,6 @@ HashStore::WriteFile()
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Need to close the inputstream here *before* rewriting its file.
|
||||
// Windows will fail with an access violation if we don't.
|
||||
if (mInputStream) {
|
||||
rv = mInputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile,
|
||||
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
|
||||
@ -877,32 +854,6 @@ HashStore::WriteFile()
|
||||
rv = safeOut->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int64_t fileSize;
|
||||
rv = storeFile->GetFileSize(&fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Reopen the file now that we've rewritten it.
|
||||
nsCOMPtr<nsIInputStream> origStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
|
||||
PR_RDONLY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream,
|
||||
fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::FinishUpdate()
|
||||
{
|
||||
// Drop add/sub data, it's only used during updates.
|
||||
mAddPrefixes.Clear();
|
||||
mSubPrefixes.Clear();
|
||||
mAddCompletes.Clear();
|
||||
mSubCompletes.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -111,23 +111,17 @@ public:
|
||||
// have a mess on your hands.
|
||||
nsresult WriteFile();
|
||||
|
||||
// Drop memory used during the update process.
|
||||
nsresult FinishUpdate();
|
||||
|
||||
// Force the entire store in memory
|
||||
nsresult ReadEntireStore();
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
nsresult Reset();
|
||||
|
||||
nsresult ReadHeader();
|
||||
// Force the entire store in memory
|
||||
nsresult ReadEntireStore();
|
||||
nsresult SanityCheck(nsIFile* aStoreFile);
|
||||
nsresult CalculateChecksum(nsAutoCString& aChecksum, bool aChecksumPresent);
|
||||
nsresult CheckChecksum(nsIFile* aStoreFile);
|
||||
void UpdateHeader();
|
||||
|
||||
nsresult EnsureChunkNumbers();
|
||||
nsresult ReadChunkNumbers();
|
||||
nsresult ReadHashes();
|
||||
nsresult ReadAddPrefixes();
|
||||
@ -158,7 +152,6 @@ private:
|
||||
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
|
||||
bool haveChunks;
|
||||
ChunkSet mAddChunks;
|
||||
ChunkSet mSubChunks;
|
||||
|
||||
|
@ -1,41 +1,7 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "LookupCache.h"
|
||||
#include "HashStore.h"
|
||||
@ -116,7 +82,8 @@ LookupCache::Open()
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile,
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
|
||||
PR_RDONLY);
|
||||
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
@ -125,18 +92,23 @@ LookupCache::Open()
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
// Simply lacking a .cache file is a recoverable error,
|
||||
// as unlike the .pset/.sbstore files it is a pure cache.
|
||||
// Just create a new empty one.
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
} else {
|
||||
// Read in the .cache file
|
||||
rv = ReadHeader(inputStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG(("ReadCompletions"));
|
||||
rv = ReadCompletions(inputStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = inputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = ReadHeader();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("ReadCompletions"));
|
||||
rv = ReadCompletions();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Loading PrefixSet"));
|
||||
rv = LoadPrefixSet();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -144,6 +116,12 @@ LookupCache::Open()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::UpdateDirHandle(nsIFile* aStoreDirectory)
|
||||
{
|
||||
return aStoreDirectory->Clone(getter_AddRefs(mStoreDirectory));
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Reset()
|
||||
{
|
||||
@ -261,13 +239,6 @@ LookupCache::WriteFile()
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Need to close the inputstream here *before* rewriting its file.
|
||||
// Windows will fail if we don't.
|
||||
if (mInputStream) {
|
||||
rv = mInputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
|
||||
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
|
||||
@ -290,11 +261,6 @@ LookupCache::WriteFile()
|
||||
rv = EnsureSizeConsistent();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Reopen the file now that we've rewritten it.
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile,
|
||||
PR_RDONLY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> psFile;
|
||||
rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -353,20 +319,20 @@ LookupCache::EnsureSizeConsistent()
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadHeader()
|
||||
LookupCache::ReadHeader(nsIInputStream* aInputStream)
|
||||
{
|
||||
if (!mInputStream) {
|
||||
if (!aInputStream) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
void *buffer = &mHeader;
|
||||
rv = NS_ReadInputStreamToBuffer(mInputStream,
|
||||
rv = NS_ReadInputStreamToBuffer(aInputStream,
|
||||
&buffer,
|
||||
sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -385,18 +351,18 @@ LookupCache::ReadHeader()
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadCompletions()
|
||||
LookupCache::ReadCompletions(nsIInputStream* aInputStream)
|
||||
{
|
||||
if (!mHeader.numCompletions) {
|
||||
mCompletions.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(mInputStream, &mCompletions, mHeader.numCompletions);
|
||||
rv = ReadTArray(aInputStream, &mCompletions, mHeader.numCompletions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Read %d completions", mCompletions.Length()));
|
||||
@ -763,11 +729,15 @@ LookupCache::LoadPrefixSet()
|
||||
if (exists) {
|
||||
LOG(("stored PrefixSet exists, loading from disk"));
|
||||
rv = mPrefixSet->LoadFromFile(psFile);
|
||||
}
|
||||
if (!exists || NS_FAILED(rv)) {
|
||||
LOG(("no (usable) stored PrefixSet found"));
|
||||
} else {
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
Reset();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
mPrimed = true;
|
||||
} else {
|
||||
LOG(("no (usable) stored PrefixSet found"));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "prlog.h"
|
||||
|
||||
@ -106,6 +107,9 @@ public:
|
||||
|
||||
nsresult Init();
|
||||
nsresult Open();
|
||||
// The directory handle where we operate will
|
||||
// be moved away when a backup is made.
|
||||
nsresult UpdateDirHandle(nsIFile* aStoreDirectory);
|
||||
// This will Clear() the passed arrays when done.
|
||||
nsresult Build(AddPrefixArray& aAddPrefixes,
|
||||
AddCompleteArray& aAddCompletes);
|
||||
@ -123,13 +127,12 @@ public:
|
||||
bool IsPrimed();
|
||||
|
||||
private:
|
||||
|
||||
void Clear();
|
||||
nsresult Reset();
|
||||
void UpdateHeader();
|
||||
nsresult ReadHeader();
|
||||
nsresult ReadHeader(nsIInputStream* aInputStream);
|
||||
nsresult ReadCompletions(nsIInputStream* aInputStream);
|
||||
nsresult EnsureSizeConsistent();
|
||||
nsresult ReadCompletions();
|
||||
nsresult LoadPrefixSet();
|
||||
// Construct a Prefix Set with known prefixes.
|
||||
// This will Clear() aAddPrefixes when done.
|
||||
@ -146,7 +149,6 @@ private:
|
||||
bool mPerClientRandomize;
|
||||
nsCString mTableName;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
CompletionArray mCompletions;
|
||||
// Set of prefixes known to be in the database
|
||||
nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
|
||||
|
@ -352,7 +352,7 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
|
||||
if (indexSize == 0) {
|
||||
LOG(("stored PrefixSet is empty!"));
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (deltaSize > (indexSize * DELTAS_LIMIT)) {
|
||||
@ -377,7 +377,7 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
mHasPrefixes = true;
|
||||
} else {
|
||||
LOG(("Version magic mismatch, not loading"));
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
LOG(("Loading PrefixSet successful"));
|
||||
|
Loading…
Reference in New Issue
Block a user