1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/url-classifier/Classifier.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,761 @@ 1.4 +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "Classifier.h" 1.10 +#include "nsIPrefBranch.h" 1.11 +#include "nsIPrefService.h" 1.12 +#include "nsISimpleEnumerator.h" 1.13 +#include "nsIRandomGenerator.h" 1.14 +#include "nsIInputStream.h" 1.15 +#include "nsISeekableStream.h" 1.16 +#include "nsIFile.h" 1.17 +#include "nsAutoPtr.h" 1.18 +#include "mozilla/Telemetry.h" 1.19 +#include "prlog.h" 1.20 + 1.21 +// NSPR_LOG_MODULES=UrlClassifierDbService:5 1.22 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; 1.23 +#if defined(PR_LOGGING) 1.24 +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) 1.25 +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) 1.26 +#else 1.27 +#define LOG(args) 1.28 +#define LOG_ENABLED() (false) 1.29 +#endif 1.30 + 1.31 +#define STORE_DIRECTORY NS_LITERAL_CSTRING("safebrowsing") 1.32 +#define TO_DELETE_DIR_SUFFIX NS_LITERAL_CSTRING("-to_delete") 1.33 +#define BACKUP_DIR_SUFFIX NS_LITERAL_CSTRING("-backup") 1.34 + 1.35 +namespace mozilla { 1.36 +namespace safebrowsing { 1.37 + 1.38 +void 1.39 +Classifier::SplitTables(const nsACString& str, nsTArray<nsCString>& tables) 1.40 +{ 1.41 + tables.Clear(); 1.42 + 1.43 + nsACString::const_iterator begin, iter, end; 1.44 + str.BeginReading(begin); 1.45 + str.EndReading(end); 1.46 + while (begin != end) { 1.47 + iter = begin; 1.48 + FindCharInReadable(',', iter, end); 1.49 + nsDependentCSubstring table = Substring(begin,iter); 1.50 + if (!table.IsEmpty()) { 1.51 + tables.AppendElement(Substring(begin, iter)); 1.52 + } 1.53 + begin = iter; 1.54 + if (begin != end) { 1.55 + begin++; 1.56 + } 1.57 + } 1.58 +} 1.59 + 1.60 +Classifier::Classifier() 1.61 + : mFreshTime(45 * 60) 1.62 +{ 1.63 +} 1.64 + 1.65 +Classifier::~Classifier() 1.66 +{ 1.67 + Close(); 1.68 +} 1.69 + 1.70 +nsresult 1.71 +Classifier::SetupPathNames() 1.72 +{ 1.73 + // Get the root directory where to store all the databases. 1.74 + nsresult rv = mCacheDirectory->Clone(getter_AddRefs(mStoreDirectory)); 1.75 + NS_ENSURE_SUCCESS(rv, rv); 1.76 + 1.77 + rv = mStoreDirectory->AppendNative(STORE_DIRECTORY); 1.78 + NS_ENSURE_SUCCESS(rv, rv); 1.79 + 1.80 + // Make sure LookupCaches (which are persistent and survive updates) 1.81 + // are reading/writing in the right place. We will be moving their 1.82 + // files "underneath" them during backup/restore. 1.83 + for (uint32_t i = 0; i < mLookupCaches.Length(); i++) { 1.84 + mLookupCaches[i]->UpdateDirHandle(mStoreDirectory); 1.85 + } 1.86 + 1.87 + // Directory where to move a backup before an update. 1.88 + rv = mCacheDirectory->Clone(getter_AddRefs(mBackupDirectory)); 1.89 + NS_ENSURE_SUCCESS(rv, rv); 1.90 + 1.91 + rv = mBackupDirectory->AppendNative(STORE_DIRECTORY + BACKUP_DIR_SUFFIX); 1.92 + NS_ENSURE_SUCCESS(rv, rv); 1.93 + 1.94 + // Directory where to move the backup so we can atomically 1.95 + // delete (really move) it. 1.96 + rv = mCacheDirectory->Clone(getter_AddRefs(mToDeleteDirectory)); 1.97 + NS_ENSURE_SUCCESS(rv, rv); 1.98 + 1.99 + rv = mToDeleteDirectory->AppendNative(STORE_DIRECTORY + TO_DELETE_DIR_SUFFIX); 1.100 + NS_ENSURE_SUCCESS(rv, rv); 1.101 + 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +nsresult 1.106 +Classifier::CreateStoreDirectory() 1.107 +{ 1.108 + // Ensure the safebrowsing directory exists. 1.109 + bool storeExists; 1.110 + nsresult rv = mStoreDirectory->Exists(&storeExists); 1.111 + NS_ENSURE_SUCCESS(rv, rv); 1.112 + 1.113 + if (!storeExists) { 1.114 + rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); 1.115 + NS_ENSURE_SUCCESS(rv, rv); 1.116 + } else { 1.117 + bool storeIsDir; 1.118 + rv = mStoreDirectory->IsDirectory(&storeIsDir); 1.119 + NS_ENSURE_SUCCESS(rv, rv); 1.120 + if (!storeIsDir) 1.121 + return NS_ERROR_FILE_DESTINATION_NOT_DIR; 1.122 + } 1.123 + 1.124 + return NS_OK; 1.125 +} 1.126 + 1.127 +nsresult 1.128 +Classifier::Open(nsIFile& aCacheDirectory) 1.129 +{ 1.130 + // Remember the Local profile directory. 1.131 + nsresult rv = aCacheDirectory.Clone(getter_AddRefs(mCacheDirectory)); 1.132 + NS_ENSURE_SUCCESS(rv, rv); 1.133 + 1.134 + // Create the handles to the update and backup directories. 1.135 + rv = SetupPathNames(); 1.136 + NS_ENSURE_SUCCESS(rv, rv); 1.137 + 1.138 + // Clean up any to-delete directories that haven't been deleted yet. 1.139 + rv = CleanToDelete(); 1.140 + NS_ENSURE_SUCCESS(rv, rv); 1.141 + 1.142 + // Check whether we have an incomplete update and recover from the 1.143 + // backup if so. 1.144 + rv = RecoverBackups(); 1.145 + NS_ENSURE_SUCCESS(rv, rv); 1.146 + 1.147 + // Make sure the main store directory exists. 1.148 + rv = CreateStoreDirectory(); 1.149 + NS_ENSURE_SUCCESS(rv, rv); 1.150 + 1.151 + mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 1.152 + NS_ENSURE_SUCCESS(rv, rv); 1.153 + 1.154 + // Build the list of know urlclassifier lists 1.155 + // XXX: Disk IO potentially on the main thread during startup 1.156 + RegenActiveTables(); 1.157 + 1.158 + return NS_OK; 1.159 +} 1.160 + 1.161 +void 1.162 +Classifier::Close() 1.163 +{ 1.164 + DropStores(); 1.165 +} 1.166 + 1.167 +void 1.168 +Classifier::Reset() 1.169 +{ 1.170 + DropStores(); 1.171 + 1.172 + mStoreDirectory->Remove(true); 1.173 + mBackupDirectory->Remove(true); 1.174 + mToDeleteDirectory->Remove(true); 1.175 + 1.176 + CreateStoreDirectory(); 1.177 + 1.178 + mTableFreshness.Clear(); 1.179 + RegenActiveTables(); 1.180 +} 1.181 + 1.182 +void 1.183 +Classifier::TableRequest(nsACString& aResult) 1.184 +{ 1.185 + nsTArray<nsCString> tables; 1.186 + ActiveTables(tables); 1.187 + for (uint32_t i = 0; i < tables.Length(); i++) { 1.188 + nsAutoPtr<HashStore> store(new HashStore(tables[i], mStoreDirectory)); 1.189 + if (!store) 1.190 + continue; 1.191 + 1.192 + nsresult rv = store->Open(); 1.193 + if (NS_FAILED(rv)) 1.194 + continue; 1.195 + 1.196 + aResult.Append(store->TableName()); 1.197 + aResult.Append(";"); 1.198 + 1.199 + ChunkSet &adds = store->AddChunks(); 1.200 + ChunkSet &subs = store->SubChunks(); 1.201 + 1.202 + if (adds.Length() > 0) { 1.203 + aResult.Append("a:"); 1.204 + nsAutoCString addList; 1.205 + adds.Serialize(addList); 1.206 + aResult.Append(addList); 1.207 + } 1.208 + 1.209 + if (subs.Length() > 0) { 1.210 + if (adds.Length() > 0) 1.211 + aResult.Append(':'); 1.212 + aResult.Append("s:"); 1.213 + nsAutoCString subList; 1.214 + subs.Serialize(subList); 1.215 + aResult.Append(subList); 1.216 + } 1.217 + 1.218 + aResult.Append('\n'); 1.219 + } 1.220 +} 1.221 + 1.222 +nsresult 1.223 +Classifier::Check(const nsACString& aSpec, 1.224 + const nsACString& aTables, 1.225 + LookupResultArray& aResults) 1.226 +{ 1.227 + Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_CHECK_TIME> timer; 1.228 + 1.229 + // Get the set of fragments based on the url. This is necessary because we 1.230 + // only look up at most 5 URLs per aSpec, even if aSpec has more than 5 1.231 + // components. 1.232 + nsTArray<nsCString> fragments; 1.233 + nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments); 1.234 + NS_ENSURE_SUCCESS(rv, rv); 1.235 + 1.236 + nsTArray<nsCString> activeTables; 1.237 + SplitTables(aTables, activeTables); 1.238 + 1.239 + nsTArray<LookupCache*> cacheArray; 1.240 + for (uint32_t i = 0; i < activeTables.Length(); i++) { 1.241 + LOG(("Checking table %s", activeTables[i].get())); 1.242 + LookupCache *cache = GetLookupCache(activeTables[i]); 1.243 + if (cache) { 1.244 + cacheArray.AppendElement(cache); 1.245 + } else { 1.246 + return NS_ERROR_FAILURE; 1.247 + } 1.248 + } 1.249 + 1.250 + // Now check each lookup fragment against the entries in the DB. 1.251 + for (uint32_t i = 0; i < fragments.Length(); i++) { 1.252 + Completion lookupHash; 1.253 + lookupHash.FromPlaintext(fragments[i], mCryptoHash); 1.254 + 1.255 + // Get list of host keys to look up 1.256 + Completion hostKey; 1.257 + rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash); 1.258 + if (NS_FAILED(rv)) { 1.259 + // Local host on the network. 1.260 + continue; 1.261 + } 1.262 + 1.263 +#if DEBUG && defined(PR_LOGGING) 1.264 + if (LOG_ENABLED()) { 1.265 + nsAutoCString checking; 1.266 + lookupHash.ToHexString(checking); 1.267 + LOG(("Checking fragment %s, hash %s (%X)", fragments[i].get(), 1.268 + checking.get(), lookupHash.ToUint32())); 1.269 + } 1.270 +#endif 1.271 + for (uint32_t i = 0; i < cacheArray.Length(); i++) { 1.272 + LookupCache *cache = cacheArray[i]; 1.273 + bool has, complete; 1.274 + rv = cache->Has(lookupHash, &has, &complete); 1.275 + NS_ENSURE_SUCCESS(rv, rv); 1.276 + if (has) { 1.277 + LookupResult *result = aResults.AppendElement(); 1.278 + if (!result) 1.279 + return NS_ERROR_OUT_OF_MEMORY; 1.280 + 1.281 + int64_t age; 1.282 + bool found = mTableFreshness.Get(cache->TableName(), &age); 1.283 + if (!found) { 1.284 + age = 24 * 60 * 60; // just a large number 1.285 + } else { 1.286 + int64_t now = (PR_Now() / PR_USEC_PER_SEC); 1.287 + age = now - age; 1.288 + } 1.289 + 1.290 + LOG(("Found a result in %s: %s (Age: %Lds)", 1.291 + cache->TableName().get(), 1.292 + complete ? "complete." : "Not complete.", 1.293 + age)); 1.294 + 1.295 + result->hash.complete = lookupHash; 1.296 + result->mComplete = complete; 1.297 + result->mFresh = (age < mFreshTime); 1.298 + result->mTableName.Assign(cache->TableName()); 1.299 + } 1.300 + } 1.301 + 1.302 + } 1.303 + 1.304 + return NS_OK; 1.305 +} 1.306 + 1.307 +nsresult 1.308 +Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates) 1.309 +{ 1.310 + Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_UPDATE_TIME> timer; 1.311 + 1.312 +#if defined(PR_LOGGING) 1.313 + PRIntervalTime clockStart = 0; 1.314 + if (LOG_ENABLED() || true) { 1.315 + clockStart = PR_IntervalNow(); 1.316 + } 1.317 +#endif 1.318 + 1.319 + LOG(("Backup before update.")); 1.320 + 1.321 + nsresult rv = BackupTables(); 1.322 + NS_ENSURE_SUCCESS(rv, rv); 1.323 + 1.324 + LOG(("Applying table updates.")); 1.325 + 1.326 + for (uint32_t i = 0; i < aUpdates->Length(); i++) { 1.327 + // Previous ApplyTableUpdates() may have consumed this update.. 1.328 + if ((*aUpdates)[i]) { 1.329 + // Run all updates for one table 1.330 + nsCString updateTable(aUpdates->ElementAt(i)->TableName()); 1.331 + rv = ApplyTableUpdates(aUpdates, updateTable); 1.332 + if (NS_FAILED(rv)) { 1.333 + if (rv != NS_ERROR_OUT_OF_MEMORY) { 1.334 + Reset(); 1.335 + } 1.336 + return rv; 1.337 + } 1.338 + } 1.339 + } 1.340 + aUpdates->Clear(); 1.341 + 1.342 + rv = RegenActiveTables(); 1.343 + NS_ENSURE_SUCCESS(rv, rv); 1.344 + 1.345 + LOG(("Cleaning up backups.")); 1.346 + 1.347 + // Move the backup directory away (signaling the transaction finished 1.348 + // successfully). This is atomic. 1.349 + rv = RemoveBackupTables(); 1.350 + NS_ENSURE_SUCCESS(rv, rv); 1.351 + 1.352 + // Do the actual deletion of the backup files. 1.353 + rv = CleanToDelete(); 1.354 + NS_ENSURE_SUCCESS(rv, rv); 1.355 + 1.356 + LOG(("Done applying updates.")); 1.357 + 1.358 +#if defined(PR_LOGGING) 1.359 + if (LOG_ENABLED() || true) { 1.360 + PRIntervalTime clockEnd = PR_IntervalNow(); 1.361 + LOG(("update took %dms\n", 1.362 + PR_IntervalToMilliseconds(clockEnd - clockStart))); 1.363 + } 1.364 +#endif 1.365 + 1.366 + return NS_OK; 1.367 +} 1.368 + 1.369 +nsresult 1.370 +Classifier::MarkSpoiled(nsTArray<nsCString>& aTables) 1.371 +{ 1.372 + for (uint32_t i = 0; i < aTables.Length(); i++) { 1.373 + LOG(("Spoiling table: %s", aTables[i].get())); 1.374 + // Spoil this table by marking it as no known freshness 1.375 + mTableFreshness.Remove(aTables[i]); 1.376 + // Remove any cached Completes for this table 1.377 + LookupCache *cache = GetLookupCache(aTables[i]); 1.378 + if (cache) { 1.379 + cache->ClearCompleteCache(); 1.380 + } 1.381 + } 1.382 + return NS_OK; 1.383 +} 1.384 + 1.385 +void 1.386 +Classifier::DropStores() 1.387 +{ 1.388 + for (uint32_t i = 0; i < mHashStores.Length(); i++) { 1.389 + delete mHashStores[i]; 1.390 + } 1.391 + mHashStores.Clear(); 1.392 + for (uint32_t i = 0; i < mLookupCaches.Length(); i++) { 1.393 + delete mLookupCaches[i]; 1.394 + } 1.395 + mLookupCaches.Clear(); 1.396 +} 1.397 + 1.398 +nsresult 1.399 +Classifier::RegenActiveTables() 1.400 +{ 1.401 + mActiveTablesCache.Clear(); 1.402 + 1.403 + nsTArray<nsCString> foundTables; 1.404 + ScanStoreDir(foundTables); 1.405 + 1.406 + for (uint32_t i = 0; i < foundTables.Length(); i++) { 1.407 + nsAutoPtr<HashStore> store(new HashStore(nsCString(foundTables[i]), mStoreDirectory)); 1.408 + if (!store) 1.409 + return NS_ERROR_OUT_OF_MEMORY; 1.410 + 1.411 + nsresult rv = store->Open(); 1.412 + if (NS_FAILED(rv)) 1.413 + continue; 1.414 + 1.415 + LookupCache *lookupCache = GetLookupCache(store->TableName()); 1.416 + if (!lookupCache) { 1.417 + continue; 1.418 + } 1.419 + 1.420 + if (!lookupCache->IsPrimed()) 1.421 + continue; 1.422 + 1.423 + const ChunkSet &adds = store->AddChunks(); 1.424 + const ChunkSet &subs = store->SubChunks(); 1.425 + 1.426 + if (adds.Length() == 0 && subs.Length() == 0) 1.427 + continue; 1.428 + 1.429 + LOG(("Active table: %s", store->TableName().get())); 1.430 + mActiveTablesCache.AppendElement(store->TableName()); 1.431 + } 1.432 + 1.433 + return NS_OK; 1.434 +} 1.435 + 1.436 +nsresult 1.437 +Classifier::ScanStoreDir(nsTArray<nsCString>& aTables) 1.438 +{ 1.439 + nsCOMPtr<nsISimpleEnumerator> entries; 1.440 + nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries)); 1.441 + NS_ENSURE_SUCCESS(rv, rv); 1.442 + 1.443 + bool hasMore; 1.444 + while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { 1.445 + nsCOMPtr<nsISupports> supports; 1.446 + rv = entries->GetNext(getter_AddRefs(supports)); 1.447 + NS_ENSURE_SUCCESS(rv, rv); 1.448 + 1.449 + nsCOMPtr<nsIFile> file = do_QueryInterface(supports); 1.450 + 1.451 + nsCString leafName; 1.452 + rv = file->GetNativeLeafName(leafName); 1.453 + NS_ENSURE_SUCCESS(rv, rv); 1.454 + 1.455 + nsCString suffix(NS_LITERAL_CSTRING(".sbstore")); 1.456 + 1.457 + int32_t dot = leafName.RFind(suffix, 0); 1.458 + if (dot != -1) { 1.459 + leafName.Cut(dot, suffix.Length()); 1.460 + aTables.AppendElement(leafName); 1.461 + } 1.462 + } 1.463 + NS_ENSURE_SUCCESS(rv, rv); 1.464 + 1.465 + return NS_OK; 1.466 +} 1.467 + 1.468 +nsresult 1.469 +Classifier::ActiveTables(nsTArray<nsCString>& aTables) 1.470 +{ 1.471 + aTables = mActiveTablesCache; 1.472 + return NS_OK; 1.473 +} 1.474 + 1.475 +nsresult 1.476 +Classifier::CleanToDelete() 1.477 +{ 1.478 + bool exists; 1.479 + nsresult rv = mToDeleteDirectory->Exists(&exists); 1.480 + NS_ENSURE_SUCCESS(rv, rv); 1.481 + 1.482 + if (exists) { 1.483 + rv = mToDeleteDirectory->Remove(true); 1.484 + NS_ENSURE_SUCCESS(rv, rv); 1.485 + } 1.486 + 1.487 + return NS_OK; 1.488 +} 1.489 + 1.490 +nsresult 1.491 +Classifier::BackupTables() 1.492 +{ 1.493 + // We have to work in reverse here: first move the normal directory 1.494 + // away to be the backup directory, then copy the files over 1.495 + // to the normal directory. This ensures that if we crash the backup 1.496 + // dir always has a valid, complete copy, instead of a partial one, 1.497 + // because that's the one we will copy over the normal store dir. 1.498 + 1.499 + nsCString backupDirName; 1.500 + nsresult rv = mBackupDirectory->GetNativeLeafName(backupDirName); 1.501 + NS_ENSURE_SUCCESS(rv, rv); 1.502 + 1.503 + nsCString storeDirName; 1.504 + rv = mStoreDirectory->GetNativeLeafName(storeDirName); 1.505 + NS_ENSURE_SUCCESS(rv, rv); 1.506 + 1.507 + rv = mStoreDirectory->MoveToNative(nullptr, backupDirName); 1.508 + NS_ENSURE_SUCCESS(rv, rv); 1.509 + 1.510 + rv = mStoreDirectory->CopyToNative(nullptr, storeDirName); 1.511 + NS_ENSURE_SUCCESS(rv, rv); 1.512 + 1.513 + // We moved some things to new places, so move the handles around, too. 1.514 + rv = SetupPathNames(); 1.515 + NS_ENSURE_SUCCESS(rv, rv); 1.516 + 1.517 + return NS_OK; 1.518 +} 1.519 + 1.520 +nsresult 1.521 +Classifier::RemoveBackupTables() 1.522 +{ 1.523 + nsCString toDeleteName; 1.524 + nsresult rv = mToDeleteDirectory->GetNativeLeafName(toDeleteName); 1.525 + NS_ENSURE_SUCCESS(rv, rv); 1.526 + 1.527 + rv = mBackupDirectory->MoveToNative(nullptr, toDeleteName); 1.528 + NS_ENSURE_SUCCESS(rv, rv); 1.529 + 1.530 + // mBackupDirectory now points to toDelete, fix that up. 1.531 + rv = SetupPathNames(); 1.532 + NS_ENSURE_SUCCESS(rv, rv); 1.533 + 1.534 + return NS_OK; 1.535 +} 1.536 + 1.537 +nsresult 1.538 +Classifier::RecoverBackups() 1.539 +{ 1.540 + bool backupExists; 1.541 + nsresult rv = mBackupDirectory->Exists(&backupExists); 1.542 + NS_ENSURE_SUCCESS(rv, rv); 1.543 + 1.544 + if (backupExists) { 1.545 + // Remove the safebrowsing dir if it exists 1.546 + nsCString storeDirName; 1.547 + rv = mStoreDirectory->GetNativeLeafName(storeDirName); 1.548 + NS_ENSURE_SUCCESS(rv, rv); 1.549 + 1.550 + bool storeExists; 1.551 + rv = mStoreDirectory->Exists(&storeExists); 1.552 + NS_ENSURE_SUCCESS(rv, rv); 1.553 + 1.554 + if (storeExists) { 1.555 + rv = mStoreDirectory->Remove(true); 1.556 + NS_ENSURE_SUCCESS(rv, rv); 1.557 + } 1.558 + 1.559 + // Move the backup to the store location 1.560 + rv = mBackupDirectory->MoveToNative(nullptr, storeDirName); 1.561 + NS_ENSURE_SUCCESS(rv, rv); 1.562 + 1.563 + // mBackupDirectory now points to storeDir, fix up. 1.564 + rv = SetupPathNames(); 1.565 + NS_ENSURE_SUCCESS(rv, rv); 1.566 + } 1.567 + 1.568 + return NS_OK; 1.569 +} 1.570 + 1.571 +/* 1.572 + * This will consume+delete updates from the passed nsTArray. 1.573 +*/ 1.574 +nsresult 1.575 +Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates, 1.576 + const nsACString& aTable) 1.577 +{ 1.578 + LOG(("Classifier::ApplyTableUpdates(%s)", PromiseFlatCString(aTable).get())); 1.579 + 1.580 + nsAutoPtr<HashStore> store(new HashStore(aTable, mStoreDirectory)); 1.581 + 1.582 + if (!store) 1.583 + return NS_ERROR_FAILURE; 1.584 + 1.585 + // take the quick exit if there is no valid update for us 1.586 + // (common case) 1.587 + uint32_t validupdates = 0; 1.588 + 1.589 + for (uint32_t i = 0; i < aUpdates->Length(); i++) { 1.590 + TableUpdate *update = aUpdates->ElementAt(i); 1.591 + if (!update || !update->TableName().Equals(store->TableName())) 1.592 + continue; 1.593 + if (update->Empty()) { 1.594 + aUpdates->ElementAt(i) = nullptr; 1.595 + delete update; 1.596 + continue; 1.597 + } 1.598 + validupdates++; 1.599 + } 1.600 + 1.601 + if (!validupdates) { 1.602 + // This can happen if the update was only valid for one table. 1.603 + return NS_OK; 1.604 + } 1.605 + 1.606 + nsresult rv = store->Open(); 1.607 + NS_ENSURE_SUCCESS(rv, rv); 1.608 + rv = store->BeginUpdate(); 1.609 + NS_ENSURE_SUCCESS(rv, rv); 1.610 + 1.611 + // Read the part of the store that is (only) in the cache 1.612 + LookupCache *prefixSet = GetLookupCache(store->TableName()); 1.613 + if (!prefixSet) { 1.614 + return NS_ERROR_FAILURE; 1.615 + } 1.616 + nsTArray<uint32_t> AddPrefixHashes; 1.617 + rv = prefixSet->GetPrefixes(&AddPrefixHashes); 1.618 + NS_ENSURE_SUCCESS(rv, rv); 1.619 + rv = store->AugmentAdds(AddPrefixHashes); 1.620 + NS_ENSURE_SUCCESS(rv, rv); 1.621 + AddPrefixHashes.Clear(); 1.622 + 1.623 + uint32_t applied = 0; 1.624 + bool updateFreshness = false; 1.625 + bool hasCompletes = false; 1.626 + 1.627 + for (uint32_t i = 0; i < aUpdates->Length(); i++) { 1.628 + TableUpdate *update = aUpdates->ElementAt(i); 1.629 + if (!update || !update->TableName().Equals(store->TableName())) 1.630 + continue; 1.631 + 1.632 + rv = store->ApplyUpdate(*update); 1.633 + NS_ENSURE_SUCCESS(rv, rv); 1.634 + 1.635 + applied++; 1.636 + 1.637 + LOG(("Applied update to table %s:", store->TableName().get())); 1.638 + LOG((" %d add chunks", update->AddChunks().Length())); 1.639 + LOG((" %d add prefixes", update->AddPrefixes().Length())); 1.640 + LOG((" %d add completions", update->AddCompletes().Length())); 1.641 + LOG((" %d sub chunks", update->SubChunks().Length())); 1.642 + LOG((" %d sub prefixes", update->SubPrefixes().Length())); 1.643 + LOG((" %d sub completions", update->SubCompletes().Length())); 1.644 + LOG((" %d add expirations", update->AddExpirations().Length())); 1.645 + LOG((" %d sub expirations", update->SubExpirations().Length())); 1.646 + 1.647 + if (!update->IsLocalUpdate()) { 1.648 + updateFreshness = true; 1.649 + LOG(("Remote update, updating freshness")); 1.650 + } 1.651 + 1.652 + if (update->AddCompletes().Length() > 0 1.653 + || update->SubCompletes().Length() > 0) { 1.654 + hasCompletes = true; 1.655 + LOG(("Contains Completes, keeping cache.")); 1.656 + } 1.657 + 1.658 + aUpdates->ElementAt(i) = nullptr; 1.659 + delete update; 1.660 + } 1.661 + 1.662 + LOG(("Applied %d update(s) to %s.", applied, store->TableName().get())); 1.663 + 1.664 + rv = store->Rebuild(); 1.665 + NS_ENSURE_SUCCESS(rv, rv); 1.666 + 1.667 + // Not an update with Completes, clear all completes data. 1.668 + if (!hasCompletes) { 1.669 + store->ClearCompletes(); 1.670 + } 1.671 + 1.672 + LOG(("Table %s now has:", store->TableName().get())); 1.673 + LOG((" %d add chunks", store->AddChunks().Length())); 1.674 + LOG((" %d add prefixes", store->AddPrefixes().Length())); 1.675 + LOG((" %d add completions", store->AddCompletes().Length())); 1.676 + LOG((" %d sub chunks", store->SubChunks().Length())); 1.677 + LOG((" %d sub prefixes", store->SubPrefixes().Length())); 1.678 + LOG((" %d sub completions", store->SubCompletes().Length())); 1.679 + 1.680 + rv = store->WriteFile(); 1.681 + NS_ENSURE_SUCCESS(rv, rv); 1.682 + 1.683 + // At this point the store is updated and written out to disk, but 1.684 + // the data is still in memory. Build our quick-lookup table here. 1.685 + rv = prefixSet->Build(store->AddPrefixes(), store->AddCompletes()); 1.686 + NS_ENSURE_SUCCESS(rv, rv); 1.687 + 1.688 +#if defined(DEBUG) && defined(PR_LOGGING) 1.689 + prefixSet->Dump(); 1.690 +#endif 1.691 + rv = prefixSet->WriteFile(); 1.692 + NS_ENSURE_SUCCESS(rv, rv); 1.693 + 1.694 + if (updateFreshness) { 1.695 + int64_t now = (PR_Now() / PR_USEC_PER_SEC); 1.696 + LOG(("Successfully updated %s", store->TableName().get())); 1.697 + mTableFreshness.Put(store->TableName(), now); 1.698 + } 1.699 + 1.700 + return NS_OK; 1.701 +} 1.702 + 1.703 +LookupCache * 1.704 +Classifier::GetLookupCache(const nsACString& aTable) 1.705 +{ 1.706 + for (uint32_t i = 0; i < mLookupCaches.Length(); i++) { 1.707 + if (mLookupCaches[i]->TableName().Equals(aTable)) { 1.708 + return mLookupCaches[i]; 1.709 + } 1.710 + } 1.711 + 1.712 + LookupCache *cache = new LookupCache(aTable, mStoreDirectory); 1.713 + nsresult rv = cache->Init(); 1.714 + if (NS_FAILED(rv)) { 1.715 + return nullptr; 1.716 + } 1.717 + rv = cache->Open(); 1.718 + if (NS_FAILED(rv)) { 1.719 + if (rv == NS_ERROR_FILE_CORRUPTED) { 1.720 + Reset(); 1.721 + } 1.722 + return nullptr; 1.723 + } 1.724 + mLookupCaches.AppendElement(cache); 1.725 + return cache; 1.726 +} 1.727 + 1.728 +nsresult 1.729 +Classifier::ReadNoiseEntries(const Prefix& aPrefix, 1.730 + const nsACString& aTableName, 1.731 + uint32_t aCount, 1.732 + PrefixArray* aNoiseEntries) 1.733 +{ 1.734 + LookupCache *cache = GetLookupCache(aTableName); 1.735 + if (!cache) { 1.736 + return NS_ERROR_FAILURE; 1.737 + } 1.738 + 1.739 + nsTArray<uint32_t> prefixes; 1.740 + nsresult rv = cache->GetPrefixes(&prefixes); 1.741 + NS_ENSURE_SUCCESS(rv, rv); 1.742 + 1.743 + uint32_t idx = prefixes.BinaryIndexOf(aPrefix.ToUint32()); 1.744 + 1.745 + if (idx == nsTArray<uint32_t>::NoIndex) { 1.746 + NS_WARNING("Could not find prefix in PrefixSet during noise lookup"); 1.747 + return NS_ERROR_FAILURE; 1.748 + } 1.749 + 1.750 + idx -= idx % aCount; 1.751 + 1.752 + for (uint32_t i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) { 1.753 + Prefix newPref; 1.754 + newPref.FromUint32(prefixes[idx+i]); 1.755 + if (newPref != aPrefix) { 1.756 + aNoiseEntries->AppendElement(newPref); 1.757 + } 1.758 + } 1.759 + 1.760 + return NS_OK; 1.761 +} 1.762 + 1.763 +} // namespace safebrowsing 1.764 +} // namespace mozilla