| Index: chrome/browser/safe_browsing/safe_browsing_database.cc
|
| diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc
|
| index d1b403b3cb7ae512a2711f7c53c7827e43db091e..236a240200a1014a73dfa8f147ea761c0da8e2fc 100644
|
| --- a/chrome/browser/safe_browsing/safe_browsing_database.cc
|
| +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc
|
| @@ -30,6 +30,8 @@
|
| #endif
|
|
|
| using content::BrowserThread;
|
| +using safe_browsing::PrefixSet;
|
| +using safe_browsing::PrefixSetBuilder;
|
|
|
| namespace {
|
|
|
| @@ -462,6 +464,163 @@ void SafeBrowsingDatabase::RecordFailure(FailureType failure_type) {
|
| FAILURE_DATABASE_MAX);
|
| }
|
|
|
| +class SafeBrowsingDatabaseNew::ThreadSafeStateManager::ReadTransaction {
|
| + public:
|
| + const SBWhitelist* GetSBWhitelist(SBWhitelistId id) {
|
| + switch (id) {
|
| + case SBWhitelistId::CSD:
|
| + return &outer_->csd_whitelist_;
|
| + case SBWhitelistId::DOWNLOAD:
|
| + return &outer_->download_whitelist_;
|
| + }
|
| + NOTREACHED();
|
| + return nullptr;
|
| + }
|
| +
|
| + const IPBlacklist* ip_blacklist() { return &outer_->ip_blacklist_; }
|
| +
|
| + const PrefixSet* GetPrefixSet(PrefixSetId id) {
|
| + switch (id) {
|
| + case PrefixSetId::BROWSE:
|
| + return outer_->browse_prefix_set_.get();
|
| + case PrefixSetId::SIDE_EFFECT_FREE_WHITELIST:
|
| + return outer_->side_effect_free_whitelist_prefix_set_.get();
|
| + case PrefixSetId::UNWANTED_SOFTWARE:
|
| + return outer_->unwanted_software_prefix_set_.get();
|
| + }
|
| + NOTREACHED();
|
| + return nullptr;
|
| + }
|
| +
|
| + PrefixGetHashCache* prefix_gethash_cache() {
|
| + // The cache is special: it is read/write on all threads. Access to it
|
| + // therefore requires a LOCK'ed transaction (i.e. it can't benefit from
|
| + // DONT_LOCK_ON_MAIN_THREAD).
|
| + DCHECK(transaction_lock_);
|
| + return &outer_->prefix_gethash_cache_;
|
| + }
|
| +
|
| + private:
|
| + // Only ThreadSafeStateManager is allowed to build a ReadTransaction.
|
| + friend class ThreadSafeStateManager;
|
| +
|
| + enum class AutoLockRequirement {
|
| + LOCK,
|
| + // SBWhitelist's, IPBlacklist's, and PrefixSet's (not caches) are only
|
| + // ever written to on the main thread (as enforced by
|
| + // ThreadSafeStateManager) and can therefore be read on the main thread
|
| + // without first acquiring |lock_|.
|
| + DONT_LOCK_ON_MAIN_THREAD
|
| + };
|
| +
|
| + ReadTransaction(const ThreadSafeStateManager* outer,
|
| + AutoLockRequirement auto_lock_requirement)
|
| + : outer_(outer) {
|
| + DCHECK(outer_);
|
| + if (auto_lock_requirement == AutoLockRequirement::LOCK)
|
| + transaction_lock_.reset(new base::AutoLock(outer_->lock_));
|
| + else
|
| + DCHECK(outer_->thread_checker_.CalledOnValidThread());
|
| + }
|
| +
|
| + const ThreadSafeStateManager* outer_;
|
| + scoped_ptr<base::AutoLock> transaction_lock_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ReadTransaction);
|
| +};
|
| +
|
| +class SafeBrowsingDatabaseNew::ThreadSafeStateManager::WriteTransaction {
|
| + public:
|
| + // Call this method if an error occured with the given whitelist. This will
|
| + // result in all lookups to the whitelist to return true.
|
| + void WhitelistEverything(SBWhitelistId id) {
|
| + SBWhitelist* whitelist = SBWhitelistForId(id);
|
| + whitelist->second = true;
|
| + whitelist->first.clear();
|
| + }
|
| +
|
| + void SwapSBWhitelist(SBWhitelistId id,
|
| + std::vector<SBFullHash>* new_whitelist) {
|
| + SBWhitelist* whitelist = SBWhitelistForId(id);
|
| + whitelist->second = false;
|
| + whitelist->first.swap(*new_whitelist);
|
| + }
|
| +
|
| + void clear_ip_blacklist() { outer_->ip_blacklist_.clear(); }
|
| +
|
| + void swap_ip_blacklist(IPBlacklist* new_blacklist) {
|
| + outer_->ip_blacklist_.swap(*new_blacklist);
|
| + }
|
| +
|
| + void SwapPrefixSet(PrefixSetId id,
|
| + scoped_ptr<const PrefixSet> new_prefix_set) {
|
| + switch (id) {
|
| + case PrefixSetId::BROWSE:
|
| + outer_->browse_prefix_set_.swap(new_prefix_set);
|
| + break;
|
| + case PrefixSetId::SIDE_EFFECT_FREE_WHITELIST:
|
| + outer_->side_effect_free_whitelist_prefix_set_.swap(new_prefix_set);
|
| + break;
|
| + case PrefixSetId::UNWANTED_SOFTWARE:
|
| + outer_->unwanted_software_prefix_set_.swap(new_prefix_set);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + void clear_prefix_gethash_cache() { outer_->prefix_gethash_cache_.clear(); }
|
| +
|
| + private:
|
| + // Only ThreadSafeStateManager is allowed to build a WriteTransaction.
|
| + friend class ThreadSafeStateManager;
|
| +
|
| + explicit WriteTransaction(ThreadSafeStateManager* outer)
|
| + : outer_(outer), transaction_lock_(outer_->lock_) {
|
| + DCHECK(outer_);
|
| + DCHECK(outer_->thread_checker_.CalledOnValidThread());
|
| + }
|
| +
|
| + SBWhitelist* SBWhitelistForId(SBWhitelistId id) {
|
| + switch (id) {
|
| + case SBWhitelistId::CSD:
|
| + return &outer_->csd_whitelist_;
|
| + case SBWhitelistId::DOWNLOAD:
|
| + return &outer_->download_whitelist_;
|
| + }
|
| + NOTREACHED();
|
| + return nullptr;
|
| + }
|
| +
|
| + ThreadSafeStateManager* outer_;
|
| + base::AutoLock transaction_lock_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(WriteTransaction);
|
| +};
|
| +
|
| +SafeBrowsingDatabaseNew::ThreadSafeStateManager::ThreadSafeStateManager(
|
| + const base::ThreadChecker& thread_checker)
|
| + : thread_checker_(thread_checker) {
|
| +}
|
| +
|
| +SafeBrowsingDatabaseNew::ThreadSafeStateManager::~ThreadSafeStateManager() {
|
| +}
|
| +
|
| +scoped_ptr<SafeBrowsingDatabaseNew::ReadTransaction>
|
| +SafeBrowsingDatabaseNew::ThreadSafeStateManager::BeginReadTransaction() {
|
| + return make_scoped_ptr(
|
| + new ReadTransaction(this, ReadTransaction::AutoLockRequirement::LOCK));
|
| +}
|
| +
|
| +scoped_ptr<SafeBrowsingDatabaseNew::ReadTransaction> SafeBrowsingDatabaseNew::
|
| + ThreadSafeStateManager::BeginReadTransactionNoLockOnMainThread() {
|
| + return make_scoped_ptr(new ReadTransaction(
|
| + this, ReadTransaction::AutoLockRequirement::DONT_LOCK_ON_MAIN_THREAD));
|
| +}
|
| +
|
| +scoped_ptr<SafeBrowsingDatabaseNew::WriteTransaction>
|
| +SafeBrowsingDatabaseNew::ThreadSafeStateManager::BeginWriteTransaction() {
|
| + return make_scoped_ptr(new WriteTransaction(this));
|
| +}
|
| +
|
| SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew()
|
| : SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile,
|
| NULL,
|
| @@ -490,7 +649,8 @@ SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew(
|
| SafeBrowsingStore* side_effect_free_whitelist_store,
|
| SafeBrowsingStore* ip_blacklist_store,
|
| SafeBrowsingStore* unwanted_software_store)
|
| - : browse_store_(browse_store),
|
| + : state_manager_(thread_checker_),
|
| + browse_store_(browse_store),
|
| download_store_(download_store),
|
| csd_whitelist_store_(csd_whitelist_store),
|
| download_whitelist_store_(download_whitelist_store),
|
| @@ -525,34 +685,57 @@ void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) {
|
| // live in memory while being actively used. The sense of enabled probably
|
| // belongs in protocol_manager or database_manager.
|
|
|
| - browse_store_->Init(
|
| - BrowseDBFilename(filename_base_),
|
| - base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
|
| - base::Unretained(this)));
|
| + {
|
| + // NOTE: A transaction here is overkill as there are no pointers to this
|
| + // class on other threads until this function returns, but it's also
|
| + // harmless as that also means there is no possibility of contention on the
|
| + // lock.
|
| + scoped_ptr<WriteTransaction> txn = state_manager_.BeginWriteTransaction();
|
| +
|
| + txn->clear_prefix_gethash_cache();
|
|
|
| - if (unwanted_software_store_.get()) {
|
| - unwanted_software_store_->Init(
|
| - UnwantedSoftwareDBFilename(filename_base_),
|
| + browse_store_->Init(
|
| + BrowseDBFilename(filename_base_),
|
| base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
|
| base::Unretained(this)));
|
| - }
|
|
|
| - {
|
| - // NOTE: There is no need to grab the lock in this function, since
|
| - // until it returns, there are no pointers to this class on other
|
| - // threads. Then again, that means there is no possibility of
|
| - // contention on the lock...
|
| - base::AutoLock locked(lookup_lock_);
|
| - prefix_gethash_cache_.clear();
|
| - LoadPrefixSet(BrowseDBFilename(filename_base_),
|
| - &browse_prefix_set_,
|
| - FAILURE_BROWSE_PREFIX_SET_READ);
|
| if (unwanted_software_store_.get()) {
|
| - LoadPrefixSet(UnwantedSoftwareDBFilename(filename_base_),
|
| - &unwanted_software_prefix_set_,
|
| + unwanted_software_store_->Init(
|
| + UnwantedSoftwareDBFilename(filename_base_),
|
| + base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
|
| + base::Unretained(this)));
|
| + }
|
| + LoadPrefixSet(BrowseDBFilename(filename_base_), txn.get(),
|
| + PrefixSetId::BROWSE, FAILURE_BROWSE_PREFIX_SET_READ);
|
| + if (unwanted_software_store_.get()) {
|
| + LoadPrefixSet(UnwantedSoftwareDBFilename(filename_base_), txn.get(),
|
| + PrefixSetId::UNWANTED_SOFTWARE,
|
| FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_READ);
|
| }
|
| +
|
| + if (side_effect_free_whitelist_store_.get()) {
|
| + const base::FilePath side_effect_free_whitelist_filename =
|
| + SideEffectFreeWhitelistDBFilename(filename_base_);
|
| + side_effect_free_whitelist_store_->Init(
|
| + side_effect_free_whitelist_filename,
|
| + base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
|
| + base::Unretained(this)));
|
| +
|
| + LoadPrefixSet(side_effect_free_whitelist_filename, txn.get(),
|
| + PrefixSetId::SIDE_EFFECT_FREE_WHITELIST,
|
| + FAILURE_SIDE_EFFECT_FREE_WHITELIST_PREFIX_SET_READ);
|
| + } else {
|
| + // Delete any files of the side-effect free sidelist that may be around
|
| + // from when it was previously enabled.
|
| + SafeBrowsingStoreFile::DeleteStore(
|
| + SideEffectFreeWhitelistDBFilename(filename_base_));
|
| + base::DeleteFile(PrefixSetForFilename(
|
| + SideEffectFreeWhitelistDBFilename(filename_base_)),
|
| + false);
|
| + }
|
| }
|
| + // Note: End the transaction early because LoadWhiteList() and
|
| + // WhitelistEverything() manage their own transactions.
|
|
|
| if (download_store_.get()) {
|
| download_store_->Init(
|
| @@ -569,12 +752,14 @@ void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) {
|
|
|
| std::vector<SBAddFullHash> full_hashes;
|
| if (csd_whitelist_store_->GetAddFullHashes(&full_hashes)) {
|
| - LoadWhitelist(full_hashes, &csd_whitelist_);
|
| + LoadWhitelist(full_hashes, SBWhitelistId::CSD);
|
| } else {
|
| - WhitelistEverything(&csd_whitelist_);
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(
|
| + SBWhitelistId::CSD);
|
| }
|
| } else {
|
| - WhitelistEverything(&csd_whitelist_); // Just to be safe.
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(
|
| + SBWhitelistId::CSD); // Just to be safe.
|
| }
|
|
|
| if (download_whitelist_store_.get()) {
|
| @@ -585,12 +770,14 @@ void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) {
|
|
|
| std::vector<SBAddFullHash> full_hashes;
|
| if (download_whitelist_store_->GetAddFullHashes(&full_hashes)) {
|
| - LoadWhitelist(full_hashes, &download_whitelist_);
|
| + LoadWhitelist(full_hashes, SBWhitelistId::DOWNLOAD);
|
| } else {
|
| - WhitelistEverything(&download_whitelist_);
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(
|
| + SBWhitelistId::DOWNLOAD);
|
| }
|
| } else {
|
| - WhitelistEverything(&download_whitelist_); // Just to be safe.
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(
|
| + SBWhitelistId::DOWNLOAD); // Just to be safe.
|
| }
|
|
|
| if (extension_blacklist_store_.get()) {
|
| @@ -600,27 +787,6 @@ void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) {
|
| base::Unretained(this)));
|
| }
|
|
|
| - if (side_effect_free_whitelist_store_.get()) {
|
| - const base::FilePath side_effect_free_whitelist_filename =
|
| - SideEffectFreeWhitelistDBFilename(filename_base_);
|
| - side_effect_free_whitelist_store_->Init(
|
| - side_effect_free_whitelist_filename,
|
| - base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
|
| - base::Unretained(this)));
|
| -
|
| - LoadPrefixSet(side_effect_free_whitelist_filename,
|
| - &side_effect_free_whitelist_prefix_set_,
|
| - FAILURE_SIDE_EFFECT_FREE_WHITELIST_PREFIX_SET_READ);
|
| - } else {
|
| - // Delete any files of the side-effect free sidelist that may be around
|
| - // from when it was previously enabled.
|
| - SafeBrowsingStoreFile::DeleteStore(
|
| - SideEffectFreeWhitelistDBFilename(filename_base_));
|
| - base::DeleteFile(
|
| - PrefixSetForFilename(SideEffectFreeWhitelistDBFilename(filename_base_)),
|
| - false);
|
| - }
|
| -
|
| if (ip_blacklist_store_.get()) {
|
| ip_blacklist_store_->Init(
|
| IpBlacklistDBFilename(filename_base_),
|
| @@ -646,17 +812,14 @@ bool SafeBrowsingDatabaseNew::ResetDatabase() {
|
| return false;
|
|
|
| // Reset objects in memory.
|
| - {
|
| - base::AutoLock locked(lookup_lock_);
|
| - prefix_gethash_cache_.clear();
|
| - browse_prefix_set_.reset();
|
| - side_effect_free_whitelist_prefix_set_.reset();
|
| - ip_blacklist_.clear();
|
| - unwanted_software_prefix_set_.reset();
|
| - }
|
| - // Wants to acquire the lock itself.
|
| - WhitelistEverything(&csd_whitelist_);
|
| - WhitelistEverything(&download_whitelist_);
|
| + scoped_ptr<WriteTransaction> txn = state_manager_.BeginWriteTransaction();
|
| + txn->clear_prefix_gethash_cache();
|
| + txn->SwapPrefixSet(PrefixSetId::BROWSE, nullptr);
|
| + txn->SwapPrefixSet(PrefixSetId::SIDE_EFFECT_FREE_WHITELIST, nullptr);
|
| + txn->SwapPrefixSet(PrefixSetId::UNWANTED_SOFTWARE, nullptr);
|
| + txn->clear_ip_blacklist();
|
| + txn->WhitelistEverything(SBWhitelistId::CSD);
|
| + txn->WhitelistEverything(SBWhitelistId::DOWNLOAD);
|
| return true;
|
| }
|
|
|
| @@ -664,21 +827,21 @@ bool SafeBrowsingDatabaseNew::ContainsBrowseUrl(
|
| const GURL& url,
|
| std::vector<SBPrefix>* prefix_hits,
|
| std::vector<SBFullHashResult>* cache_hits) {
|
| - return PrefixSetContainsUrl(
|
| - url, &browse_prefix_set_, prefix_hits, cache_hits);
|
| + return PrefixSetContainsUrl(url, PrefixSetId::BROWSE, prefix_hits,
|
| + cache_hits);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsUnwantedSoftwareUrl(
|
| const GURL& url,
|
| std::vector<SBPrefix>* prefix_hits,
|
| std::vector<SBFullHashResult>* cache_hits) {
|
| - return PrefixSetContainsUrl(
|
| - url, &unwanted_software_prefix_set_, prefix_hits, cache_hits);
|
| + return PrefixSetContainsUrl(url, PrefixSetId::UNWANTED_SOFTWARE, prefix_hits,
|
| + cache_hits);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::PrefixSetContainsUrl(
|
| const GURL& url,
|
| - scoped_ptr<const safe_browsing::PrefixSet>* prefix_set_getter,
|
| + PrefixSetId prefix_set_id,
|
| std::vector<SBPrefix>* prefix_hits,
|
| std::vector<SBFullHashResult>* cache_hits) {
|
| // Clear the results first.
|
| @@ -690,44 +853,42 @@ bool SafeBrowsingDatabaseNew::PrefixSetContainsUrl(
|
| if (full_hashes.empty())
|
| return false;
|
|
|
| - return PrefixSetContainsUrlHashes(
|
| - full_hashes, prefix_set_getter, prefix_hits, cache_hits);
|
| + return PrefixSetContainsUrlHashes(full_hashes, prefix_set_id, prefix_hits,
|
| + cache_hits);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsBrowseUrlHashesForTesting(
|
| const std::vector<SBFullHash>& full_hashes,
|
| std::vector<SBPrefix>* prefix_hits,
|
| std::vector<SBFullHashResult>* cache_hits) {
|
| - return PrefixSetContainsUrlHashes(
|
| - full_hashes, &browse_prefix_set_, prefix_hits, cache_hits);
|
| + return PrefixSetContainsUrlHashes(full_hashes, PrefixSetId::BROWSE,
|
| + prefix_hits, cache_hits);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::PrefixSetContainsUrlHashes(
|
| const std::vector<SBFullHash>& full_hashes,
|
| - scoped_ptr<const safe_browsing::PrefixSet>* prefix_set_getter,
|
| + PrefixSetId prefix_set_id,
|
| std::vector<SBPrefix>* prefix_hits,
|
| std::vector<SBFullHashResult>* cache_hits) {
|
| // Used to determine cache expiration.
|
| const base::Time now = base::Time::Now();
|
|
|
| - // This function can be called on any thread, so lock against any changes
|
| - base::AutoLock locked(lookup_lock_);
|
| -
|
| - // |prefix_set| is empty until it is either read from disk, or the first
|
| - // update populates it. Bail out without a hit if not yet available.
|
| - // |prefix_set_getter| can only be accessed while holding |lookup_lock_| hence
|
| - // why it is passed as a parameter rather than passing the |prefix_set|
|
| - // directly.
|
| - const safe_browsing::PrefixSet* prefix_set = prefix_set_getter->get();
|
| - if (!prefix_set)
|
| - return false;
|
| -
|
| - for (size_t i = 0; i < full_hashes.size(); ++i) {
|
| - if (!GetCachedFullHash(
|
| - &prefix_gethash_cache_, full_hashes[i], now, cache_hits)) {
|
| - // No valid cached result, check the database.
|
| - if (prefix_set->Exists(full_hashes[i]))
|
| - prefix_hits->push_back(full_hashes[i].prefix);
|
| + {
|
| + scoped_ptr<ReadTransaction> txn = state_manager_.BeginReadTransaction();
|
| +
|
| + // |prefix_set| is empty until it is either read from disk, or the first
|
| + // update populates it. Bail out without a hit if not yet available.
|
| + const PrefixSet* prefix_set = txn->GetPrefixSet(prefix_set_id);
|
| + if (!prefix_set)
|
| + return false;
|
| +
|
| + for (size_t i = 0; i < full_hashes.size(); ++i) {
|
| + if (!GetCachedFullHash(txn->prefix_gethash_cache(), full_hashes[i], now,
|
| + cache_hits)) {
|
| + // No valid cached result, check the database.
|
| + if (prefix_set->Exists(full_hashes[i]))
|
| + prefix_hits->push_back(full_hashes[i].prefix);
|
| + }
|
| }
|
| }
|
|
|
| @@ -759,13 +920,13 @@ bool SafeBrowsingDatabaseNew::ContainsDownloadUrl(
|
| bool SafeBrowsingDatabaseNew::ContainsCsdWhitelistedUrl(const GURL& url) {
|
| std::vector<SBFullHash> full_hashes;
|
| UrlToFullHashes(url, true, &full_hashes);
|
| - return ContainsWhitelistedHashes(csd_whitelist_, full_hashes);
|
| + return ContainsWhitelistedHashes(SBWhitelistId::CSD, full_hashes);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedUrl(const GURL& url) {
|
| std::vector<SBFullHash> full_hashes;
|
| UrlToFullHashes(url, true, &full_hashes);
|
| - return ContainsWhitelistedHashes(download_whitelist_, full_hashes);
|
| + return ContainsWhitelistedHashes(SBWhitelistId::DOWNLOAD, full_hashes);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsExtensionPrefixes(
|
| @@ -793,16 +954,18 @@ bool SafeBrowsingDatabaseNew::ContainsSideEffectFreeWhitelistUrl(
|
| url_to_check += "?" + query;
|
| SBFullHash full_hash = SBFullHashForString(url_to_check);
|
|
|
| - // This function can be called on any thread, so lock against any changes
|
| - base::AutoLock locked(lookup_lock_);
|
| + scoped_ptr<ReadTransaction> txn = state_manager_.BeginReadTransaction();
|
| +
|
| + const PrefixSet* side_effect_free_whitelist_prefix_set =
|
| + txn->GetPrefixSet(PrefixSetId::SIDE_EFFECT_FREE_WHITELIST);
|
|
|
| // |side_effect_free_whitelist_prefix_set_| is empty until it is either read
|
| // from disk, or the first update populates it. Bail out without a hit if
|
| // not yet available.
|
| - if (!side_effect_free_whitelist_prefix_set_.get())
|
| + if (!side_effect_free_whitelist_prefix_set)
|
| return false;
|
|
|
| - return side_effect_free_whitelist_prefix_set_->Exists(full_hash);
|
| + return side_effect_free_whitelist_prefix_set->Exists(full_hash);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsMalwareIP(const std::string& ip_address) {
|
| @@ -814,11 +977,10 @@ bool SafeBrowsingDatabaseNew::ContainsMalwareIP(const std::string& ip_address) {
|
| if (ip_number.size() != net::kIPv6AddressSize)
|
| return false; // better safe than sorry.
|
|
|
| - // This function can be called on any thread, so lock against any changes
|
| - base::AutoLock locked(lookup_lock_);
|
| - for (IPBlacklist::const_iterator it = ip_blacklist_.begin();
|
| - it != ip_blacklist_.end();
|
| - ++it) {
|
| + scoped_ptr<ReadTransaction> txn = state_manager_.BeginReadTransaction();
|
| + const IPBlacklist* ip_blacklist = txn->ip_blacklist();
|
| + for (IPBlacklist::const_iterator it = ip_blacklist->begin();
|
| + it != ip_blacklist->end(); ++it) {
|
| const std::string& mask = it->first;
|
| DCHECK_EQ(mask.size(), ip_number.size());
|
| std::string subnet(net::kIPv6AddressSize, '\0');
|
| @@ -842,18 +1004,19 @@ bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedString(
|
| const std::string& str) {
|
| std::vector<SBFullHash> hashes;
|
| hashes.push_back(SBFullHashForString(str));
|
| - return ContainsWhitelistedHashes(download_whitelist_, hashes);
|
| + return ContainsWhitelistedHashes(SBWhitelistId::DOWNLOAD, hashes);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::ContainsWhitelistedHashes(
|
| - const SBWhitelist& whitelist,
|
| + SBWhitelistId whitelist_id,
|
| const std::vector<SBFullHash>& hashes) {
|
| - base::AutoLock l(lookup_lock_);
|
| - if (whitelist.second)
|
| + scoped_ptr<ReadTransaction> txn = state_manager_.BeginReadTransaction();
|
| + const SBWhitelist* whitelist = txn->GetSBWhitelist(whitelist_id);
|
| + if (whitelist->second)
|
| return true;
|
| for (std::vector<SBFullHash>::const_iterator it = hashes.begin();
|
| it != hashes.end(); ++it) {
|
| - if (std::binary_search(whitelist.first.begin(), whitelist.first.end(),
|
| + if (std::binary_search(whitelist->first.begin(), whitelist->first.end(),
|
| *it, SBFullHashLess)) {
|
| return true;
|
| }
|
| @@ -1000,19 +1163,19 @@ void SafeBrowsingDatabaseNew::CacheHashResults(
|
| const base::TimeDelta& cache_lifetime) {
|
| const base::Time expire_after = base::Time::Now() + cache_lifetime;
|
|
|
| - // This function can be called on any thread, so lock against any changes
|
| - base::AutoLock locked(lookup_lock_);
|
| + scoped_ptr<ReadTransaction> txn = state_manager_.BeginReadTransaction();
|
| + PrefixGetHashCache* prefix_gethash_cache = txn->prefix_gethash_cache();
|
|
|
| // Create or reset all cached results for these prefixes.
|
| for (size_t i = 0; i < prefixes.size(); ++i) {
|
| - prefix_gethash_cache_[prefixes[i]] = SBCachedFullHashResult(expire_after);
|
| + (*prefix_gethash_cache)[prefixes[i]] = SBCachedFullHashResult(expire_after);
|
| }
|
|
|
| // Insert any fullhash hits. Note that there may be one, multiple, or no
|
| // fullhashes for any given entry in |prefixes|.
|
| for (size_t i = 0; i < full_hits.size(); ++i) {
|
| const SBPrefix prefix = full_hits[i].hash.prefix;
|
| - prefix_gethash_cache_[prefix].full_hashes.push_back(full_hits[i]);
|
| + (*prefix_gethash_cache)[prefix].full_hashes.push_back(full_hits[i]);
|
| }
|
| }
|
|
|
| @@ -1073,12 +1236,9 @@ bool SafeBrowsingDatabaseNew::UpdateStarted(
|
| return false;
|
| }
|
|
|
| - {
|
| - base::AutoLock locked(lookup_lock_);
|
| - // Cached fullhash results must be cleared on every database update (whether
|
| - // successful or not.)
|
| - prefix_gethash_cache_.clear();
|
| - }
|
| + // Cached fullhash results must be cleared on every database update (whether
|
| + // successful or not).
|
| + state_manager_.BeginWriteTransaction()->clear_prefix_gethash_cache();
|
|
|
| UpdateChunkRangesForLists(browse_store_.get(),
|
| safe_browsing_util::kMalwareList,
|
| @@ -1200,17 +1360,17 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) {
|
|
|
| UpdatePrefixSetUrlStore(BrowseDBFilename(filename_base_),
|
| browse_store_.get(),
|
| - &browse_prefix_set_,
|
| + PrefixSetId::BROWSE,
|
| FAILURE_BROWSE_DATABASE_UPDATE_FINISH,
|
| FAILURE_BROWSE_PREFIX_SET_WRITE,
|
| true);
|
|
|
| UpdateWhitelistStore(CsdWhitelistDBFilename(filename_base_),
|
| csd_whitelist_store_.get(),
|
| - &csd_whitelist_);
|
| + SBWhitelistId::CSD);
|
| UpdateWhitelistStore(DownloadWhitelistDBFilename(filename_base_),
|
| download_whitelist_store_.get(),
|
| - &download_whitelist_);
|
| + SBWhitelistId::DOWNLOAD);
|
|
|
| if (extension_blacklist_store_) {
|
| int64 size_bytes = UpdateHashPrefixStore(
|
| @@ -1224,7 +1384,7 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) {
|
| if (side_effect_free_whitelist_store_) {
|
| UpdatePrefixSetUrlStore(SideEffectFreeWhitelistDBFilename(filename_base_),
|
| side_effect_free_whitelist_store_.get(),
|
| - &side_effect_free_whitelist_prefix_set_,
|
| + PrefixSetId::SIDE_EFFECT_FREE_WHITELIST,
|
| FAILURE_SIDE_EFFECT_FREE_WHITELIST_UPDATE_FINISH,
|
| FAILURE_SIDE_EFFECT_FREE_WHITELIST_PREFIX_SET_WRITE,
|
| false);
|
| @@ -1236,7 +1396,7 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) {
|
| if (unwanted_software_store_) {
|
| UpdatePrefixSetUrlStore(UnwantedSoftwareDBFilename(filename_base_),
|
| unwanted_software_store_.get(),
|
| - &unwanted_software_prefix_set_,
|
| + PrefixSetId::UNWANTED_SOFTWARE,
|
| FAILURE_UNWANTED_SOFTWARE_DATABASE_UPDATE_FINISH,
|
| FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_WRITE,
|
| true);
|
| @@ -1246,7 +1406,7 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) {
|
| void SafeBrowsingDatabaseNew::UpdateWhitelistStore(
|
| const base::FilePath& store_filename,
|
| SafeBrowsingStore* store,
|
| - SBWhitelist* whitelist) {
|
| + SBWhitelistId whitelist_id) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
| if (!store)
|
| @@ -1254,11 +1414,11 @@ void SafeBrowsingDatabaseNew::UpdateWhitelistStore(
|
|
|
| // Note: |builder| will not be empty. The current data store implementation
|
| // stores all full-length hashes as both full and prefix hashes.
|
| - safe_browsing::PrefixSetBuilder builder;
|
| + PrefixSetBuilder builder;
|
| std::vector<SBAddFullHash> full_hashes;
|
| if (!store->FinishUpdate(&builder, &full_hashes)) {
|
| RecordFailure(FAILURE_WHITELIST_DATABASE_UPDATE_FINISH);
|
| - WhitelistEverything(whitelist);
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(whitelist_id);
|
| return;
|
| }
|
|
|
| @@ -1266,7 +1426,7 @@ void SafeBrowsingDatabaseNew::UpdateWhitelistStore(
|
| base::mac::SetFileBackupExclusion(store_filename);
|
| #endif
|
|
|
| - LoadWhitelist(full_hashes, whitelist);
|
| + LoadWhitelist(full_hashes, whitelist_id);
|
| }
|
|
|
| int64 SafeBrowsingDatabaseNew::UpdateHashPrefixStore(
|
| @@ -1277,7 +1437,7 @@ int64 SafeBrowsingDatabaseNew::UpdateHashPrefixStore(
|
|
|
| // These results are not used after this call. Simply ignore the
|
| // returned value after FinishUpdate(...).
|
| - safe_browsing::PrefixSetBuilder builder;
|
| + PrefixSetBuilder builder;
|
| std::vector<SBAddFullHash> add_full_hashes_result;
|
|
|
| if (!store->FinishUpdate(&builder, &add_full_hashes_result))
|
| @@ -1293,13 +1453,12 @@ int64 SafeBrowsingDatabaseNew::UpdateHashPrefixStore(
|
| void SafeBrowsingDatabaseNew::UpdatePrefixSetUrlStore(
|
| const base::FilePath& db_filename,
|
| SafeBrowsingStore* url_store,
|
| - scoped_ptr<const safe_browsing::PrefixSet>* prefix_set,
|
| + PrefixSetId prefix_set_id,
|
| FailureType finish_failure_type,
|
| FailureType write_failure_type,
|
| bool store_full_hashes_in_prefix_set) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| DCHECK(url_store);
|
| - DCHECK(prefix_set);
|
|
|
| // Measure the amount of IO during the filter build.
|
| base::IoCounters io_before, io_after;
|
| @@ -1323,14 +1482,14 @@ void SafeBrowsingDatabaseNew::UpdatePrefixSetUrlStore(
|
| // TODO(shess): Perhaps refactor to let builder accumulate full hashes on the
|
| // fly? Other clients use the SBAddFullHash vector, but AFAICT they only use
|
| // the SBFullHash portion. It would need an accessor on PrefixSet.
|
| - safe_browsing::PrefixSetBuilder builder;
|
| + PrefixSetBuilder builder;
|
| std::vector<SBAddFullHash> add_full_hashes;
|
| if (!url_store->FinishUpdate(&builder, &add_full_hashes)) {
|
| RecordFailure(finish_failure_type);
|
| return;
|
| }
|
|
|
| - scoped_ptr<const safe_browsing::PrefixSet> new_prefix_set;
|
| + scoped_ptr<const PrefixSet> new_prefix_set;
|
| if (store_full_hashes_in_prefix_set) {
|
| std::vector<SBFullHash> full_hash_results;
|
| for (size_t i = 0; i < add_full_hashes.size(); ++i) {
|
| @@ -1347,16 +1506,20 @@ void SafeBrowsingDatabaseNew::UpdatePrefixSetUrlStore(
|
| }
|
|
|
| // Swap in the newly built filter.
|
| - {
|
| - base::AutoLock locked(lookup_lock_);
|
| - prefix_set->swap(new_prefix_set);
|
| - }
|
| + state_manager_.BeginWriteTransaction()->SwapPrefixSet(prefix_set_id,
|
| + new_prefix_set.Pass());
|
|
|
| UMA_HISTOGRAM_LONG_TIMES("SB2.BuildFilter", base::TimeTicks::Now() - before);
|
|
|
| - // Persist the prefix set to disk. Note: there is no need to lock since the
|
| - // only write to |*prefix_set| is on this thread (in the swap() above).
|
| - WritePrefixSet(db_filename, prefix_set->get(), write_failure_type);
|
| + {
|
| + // Persist the prefix set to disk. Do not grab the lock to avoid contention
|
| + // while writing to disk. This is safe as only this thread can ever modify
|
| + // |state_manager_|'s prefix sets anyways.
|
| + scoped_ptr<ReadTransaction> txn =
|
| + state_manager_.BeginReadTransactionNoLockOnMainThread();
|
| + WritePrefixSet(db_filename, txn->GetPrefixSet(prefix_set_id),
|
| + write_failure_type);
|
| + }
|
|
|
| // Gather statistics.
|
| if (got_counters && metric->GetIOCounters(&io_after)) {
|
| @@ -1388,7 +1551,7 @@ void SafeBrowsingDatabaseNew::UpdateIpBlacklistStore() {
|
|
|
| // Note: prefixes will not be empty. The current data store implementation
|
| // stores all full-length hashes as both full and prefix hashes.
|
| - safe_browsing::PrefixSetBuilder builder;
|
| + PrefixSetBuilder builder;
|
| std::vector<SBAddFullHash> full_hashes;
|
| if (!ip_blacklist_store_->FinishUpdate(&builder, &full_hashes)) {
|
| RecordFailure(FAILURE_IP_BLACKLIST_UPDATE_FINISH);
|
| @@ -1432,19 +1595,14 @@ void SafeBrowsingDatabaseNew::OnHandleCorruptDatabase() {
|
|
|
| // TODO(shess): I'm not clear why this code doesn't have any
|
| // real error-handling.
|
| -void SafeBrowsingDatabaseNew::LoadPrefixSet(
|
| - const base::FilePath& db_filename,
|
| - scoped_ptr<const safe_browsing::PrefixSet>* prefix_set,
|
| - FailureType read_failure_type) {
|
| +void SafeBrowsingDatabaseNew::LoadPrefixSet(const base::FilePath& db_filename,
|
| + WriteTransaction* txn,
|
| + PrefixSetId prefix_set_id,
|
| + FailureType read_failure_type) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - if (!prefix_set)
|
| - return;
|
| -
|
| + DCHECK(txn);
|
| DCHECK(!filename_base_.empty());
|
|
|
| - const base::FilePath prefix_set_filename = PrefixSetForFilename(db_filename);
|
| -
|
| // Only use the prefix set if database is present and non-empty.
|
| if (!GetFileSizeOrZero(db_filename))
|
| return;
|
| @@ -1456,11 +1614,12 @@ void SafeBrowsingDatabaseNew::LoadPrefixSet(
|
| base::DeleteFile(bloom_filter_filename, false);
|
|
|
| const base::TimeTicks before = base::TimeTicks::Now();
|
| - *prefix_set = safe_browsing::PrefixSet::LoadFile(prefix_set_filename);
|
| - UMA_HISTOGRAM_TIMES("SB2.PrefixSetLoad", base::TimeTicks::Now() - before);
|
| -
|
| - if (!prefix_set->get())
|
| + scoped_ptr<const PrefixSet> new_prefix_set =
|
| + PrefixSet::LoadFile(PrefixSetForFilename(db_filename));
|
| + if (!new_prefix_set.get())
|
| RecordFailure(read_failure_type);
|
| + txn->SwapPrefixSet(prefix_set_id, new_prefix_set.Pass());
|
| + UMA_HISTOGRAM_TIMES("SB2.PrefixSetLoad", base::TimeTicks::Now() - before);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::Delete() {
|
| @@ -1543,10 +1702,9 @@ bool SafeBrowsingDatabaseNew::Delete() {
|
| return r1 && r2 && r3 && r4 && r5 && r6 && r7 && r8 && r9 && r10 && r11;
|
| }
|
|
|
| -void SafeBrowsingDatabaseNew::WritePrefixSet(
|
| - const base::FilePath& db_filename,
|
| - const safe_browsing::PrefixSet* prefix_set,
|
| - FailureType write_failure_type) {
|
| +void SafeBrowsingDatabaseNew::WritePrefixSet(const base::FilePath& db_filename,
|
| + const PrefixSet* prefix_set,
|
| + FailureType write_failure_type) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
| if (!prefix_set)
|
| @@ -1570,20 +1728,13 @@ void SafeBrowsingDatabaseNew::WritePrefixSet(
|
| #endif
|
| }
|
|
|
| -void SafeBrowsingDatabaseNew::WhitelistEverything(SBWhitelist* whitelist) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - base::AutoLock locked(lookup_lock_);
|
| - whitelist->second = true;
|
| - whitelist->first.clear();
|
| -}
|
| -
|
| void SafeBrowsingDatabaseNew::LoadWhitelist(
|
| const std::vector<SBAddFullHash>& full_hashes,
|
| - SBWhitelist* whitelist) {
|
| + SBWhitelistId whitelist_id) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
| if (full_hashes.size() > kMaxWhitelistSize) {
|
| - WhitelistEverything(whitelist);
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(whitelist_id);
|
| return;
|
| }
|
|
|
| @@ -1599,11 +1750,10 @@ void SafeBrowsingDatabaseNew::LoadWhitelist(
|
| if (std::binary_search(new_whitelist.begin(), new_whitelist.end(),
|
| kill_switch, SBFullHashLess)) {
|
| // The kill switch is whitelisted hence we whitelist all URLs.
|
| - WhitelistEverything(whitelist);
|
| + state_manager_.BeginWriteTransaction()->WhitelistEverything(whitelist_id);
|
| } else {
|
| - base::AutoLock locked(lookup_lock_);
|
| - whitelist->second = false;
|
| - whitelist->first.swap(new_whitelist);
|
| + state_manager_.BeginWriteTransaction()->SwapSBWhitelist(whitelist_id,
|
| + &new_whitelist);
|
| }
|
| }
|
|
|
| @@ -1644,18 +1794,23 @@ void SafeBrowsingDatabaseNew::LoadIpBlacklist(
|
| new_blacklist[mask].insert(hashed_ip_prefix);
|
| }
|
|
|
| - base::AutoLock locked(lookup_lock_);
|
| - ip_blacklist_.swap(new_blacklist);
|
| + state_manager_.BeginWriteTransaction()->swap_ip_blacklist(&new_blacklist);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::IsMalwareIPMatchKillSwitchOn() {
|
| SBFullHash malware_kill_switch = SBFullHashForString(kMalwareIPKillSwitchUrl);
|
| std::vector<SBFullHash> full_hashes;
|
| full_hashes.push_back(malware_kill_switch);
|
| - return ContainsWhitelistedHashes(csd_whitelist_, full_hashes);
|
| + return ContainsWhitelistedHashes(SBWhitelistId::CSD, full_hashes);
|
| }
|
|
|
| bool SafeBrowsingDatabaseNew::IsCsdWhitelistKillSwitchOn() {
|
| - base::AutoLock locked(lookup_lock_);
|
| - return csd_whitelist_.second;
|
| + return state_manager_.BeginReadTransaction()
|
| + ->GetSBWhitelist(SBWhitelistId::CSD)
|
| + ->second;
|
| +}
|
| +
|
| +SafeBrowsingDatabaseNew::PrefixGetHashCache*
|
| +SafeBrowsingDatabaseNew::GetUnsynchronizedPrefixGetHashCacheForTesting() {
|
| + return state_manager_.BeginReadTransaction()->prefix_gethash_cache();
|
| }
|
|
|