toolkit/components/url-classifier/Classifier.cpp

changeset 0
6474c204b198
     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

mercurial