Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2406)

Unified Diff: components/subresource_filter/core/browser/ruleset_service.cc

Issue 2182493009: Framework for indexing of subresource filtering rules. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Attempt #2 to fix tests on Windows. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/subresource_filter/core/browser/ruleset_service.cc
diff --git a/components/subresource_filter/core/browser/ruleset_service.cc b/components/subresource_filter/core/browser/ruleset_service.cc
index 86e3e967ec79c207243f03e9b3792cf1b3a4bcea..851563755888da4e03ac9b9336bd8d139f6eebe2 100644
--- a/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/components/subresource_filter/core/browser/ruleset_service.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback.h"
#include "base/files/file_proxy.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
@@ -22,6 +23,8 @@
#include "components/prefs/pref_service.h"
#include "components/subresource_filter/core/browser/ruleset_distributor.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
+#include "components/subresource_filter/core/common/indexed_ruleset.h"
+#include "components/subresource_filter/core/common/proto/rules.pb.h"
namespace subresource_filter {
@@ -29,9 +32,6 @@ namespace subresource_filter {
namespace {
-// The current binary format version of the serialized ruleset.
-const int kCurrentRulesetFormatVersion = 10;
-
// Names of the preferences storing the most recent ruleset version that
// was successfully stored to disk.
const char kSubresourceFilterRulesetContentVersion[] =
@@ -39,64 +39,60 @@ const char kSubresourceFilterRulesetContentVersion[] =
const char kSubresourceFilterRulesetFormatVersion[] =
"subresource_filter.ruleset_version.format";
-base::FilePath GetSubdirectoryPathForVersion(const base::FilePath& base_dir,
- const RulesetVersion& version) {
- return base_dir.AppendASCII(version.AsString());
-}
-
base::FilePath GetRulesetDataFilePath(const base::FilePath& version_directory) {
return version_directory.Append(kRulesetDataFileName);
}
} // namespace
-// RulesetVersion ------------------------------------------------------------
+// IndexedRulesetVersion ------------------------------------------------------
-RulesetVersion::RulesetVersion() = default;
-RulesetVersion::RulesetVersion(const std::string& content_version,
- int format_version)
+IndexedRulesetVersion::IndexedRulesetVersion() = default;
+IndexedRulesetVersion::IndexedRulesetVersion(const std::string& content_version,
+ int format_version)
: content_version(content_version), format_version(format_version) {}
-RulesetVersion::~RulesetVersion() = default;
-RulesetVersion& RulesetVersion::operator=(const RulesetVersion&) = default;
+IndexedRulesetVersion::~IndexedRulesetVersion() = default;
+IndexedRulesetVersion& IndexedRulesetVersion::operator=(
+ const IndexedRulesetVersion&) = default;
// static
-void RulesetVersion::RegisterPrefs(PrefRegistrySimple* registry) {
+void IndexedRulesetVersion::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(kSubresourceFilterRulesetContentVersion,
std::string());
registry->RegisterIntegerPref(kSubresourceFilterRulesetFormatVersion, 0);
}
// static
-int RulesetVersion::CurrentFormatVersion() {
- return kCurrentRulesetFormatVersion;
+int IndexedRulesetVersion::CurrentFormatVersion() {
+ return RulesetIndexer::kIndexedFormatVersion;
}
-void RulesetVersion::ReadFromPrefs(PrefService* local_state) {
+void IndexedRulesetVersion::ReadFromPrefs(PrefService* local_state) {
format_version =
local_state->GetInteger(kSubresourceFilterRulesetFormatVersion);
content_version =
local_state->GetString(kSubresourceFilterRulesetContentVersion);
}
-bool RulesetVersion::IsValid() const {
+bool IndexedRulesetVersion::IsValid() const {
return format_version != 0 && !content_version.empty();
}
-bool RulesetVersion::IsCurrentFormatVersion() const {
+bool IndexedRulesetVersion::IsCurrentFormatVersion() const {
return format_version == CurrentFormatVersion();
}
-void RulesetVersion::SaveToPrefs(PrefService* local_state) const {
- DCHECK(IsValid());
+void IndexedRulesetVersion::SaveToPrefs(PrefService* local_state) const {
local_state->SetInteger(kSubresourceFilterRulesetFormatVersion,
format_version);
local_state->SetString(kSubresourceFilterRulesetContentVersion,
content_version);
}
-std::string RulesetVersion::AsString() const {
- DCHECK(IsValid());
- return base::IntToString(format_version) + "_" + content_version;
+base::FilePath IndexedRulesetVersion::GetSubdirectoryPathForVersion(
+ const base::FilePath& base_dir) const {
+ return base_dir.AppendASCII(base::IntToString(format_version))
+ .AppendASCII(content_version);
}
// RulesetService ------------------------------------------------------------
@@ -104,32 +100,36 @@ std::string RulesetVersion::AsString() const {
RulesetService::RulesetService(
PrefService* local_state,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
- const base::FilePath& base_dir)
+ const base::FilePath& indexed_ruleset_base_dir)
: local_state_(local_state),
blocking_task_runner_(blocking_task_runner),
- base_dir_(base_dir) {
+ indexed_ruleset_base_dir_(indexed_ruleset_base_dir) {
DCHECK_NE(local_state_->GetInitializationStatus(),
PrefService::INITIALIZATION_STATUS_WAITING);
- RulesetVersion version_on_disk;
- version_on_disk.ReadFromPrefs(local_state_);
- if (version_on_disk.IsCurrentFormatVersion())
- OpenAndPublishRuleset(version_on_disk);
+ IndexedRulesetVersion most_recently_indexed_version;
+ most_recently_indexed_version.ReadFromPrefs(local_state_);
+ if (most_recently_indexed_version.IsValid() &&
+ most_recently_indexed_version.IsCurrentFormatVersion())
+ OpenAndPublishRuleset(most_recently_indexed_version);
}
RulesetService::~RulesetService() {}
-void RulesetService::StoreAndPublishUpdatedRuleset(
- std::vector<uint8_t> ruleset_data,
- const std::string& new_content_version) {
- // TODO(engedy): The |format_version| corresponds to the output of the
- // indexing step that will ultimately be performed here.
- RulesetVersion new_version(new_content_version,
- RulesetVersion::CurrentFormatVersion());
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(), FROM_HERE,
- base::Bind(&WriteRuleset, base_dir_, new_version,
- std::move(ruleset_data)),
- base::Bind(&RulesetService::OnWrittenRuleset, AsWeakPtr(), new_version));
+void RulesetService::IndexAndStoreAndPublishRulesetVersionIfNeeded(
+ const base::FilePath& unindexed_ruleset_path,
+ const std::string& content_version) {
+ // Trying to store a ruleset with the same version for a second time would not
+ // only be futile, but would fail on Windows due to "File System Tunneling" as
+ // long as the previously stored copy of the rules is still in use.
+ IndexedRulesetVersion most_recently_indexed_version;
+ most_recently_indexed_version.ReadFromPrefs(local_state_);
+ if (most_recently_indexed_version.IsCurrentFormatVersion() &&
+ most_recently_indexed_version.content_version == content_version)
+ return;
+
+ IndexAndStoreRuleset(
+ unindexed_ruleset_path, content_version,
+ base::Bind(&RulesetService::OpenAndPublishRuleset, AsWeakPtr()));
}
void RulesetService::RegisterDistributor(
@@ -139,30 +139,83 @@ void RulesetService::RegisterDistributor(
distributors_.push_back(std::move(distributor));
}
-void RulesetService::NotifyRulesetVersionAvailable(
- const std::string& rules,
- const base::Version& version) {}
+void RulesetService::IndexAndStoreRuleset(
+ const base::FilePath& unindexed_ruleset_path,
+ const std::string& content_version,
+ const WriteRulesetCallback& success_callback) {
+ IndexedRulesetVersion indexed_version;
+ indexed_version.content_version = content_version;
+ indexed_version.format_version =
+ IndexedRulesetVersion::CurrentFormatVersion();
+ if (!indexed_version.IsValid())
+ return;
+ base::PostTaskAndReplyWithResult(
+ blocking_task_runner_.get(), FROM_HERE,
+ base::Bind(&RulesetService::IndexAndWriteRuleset,
+ indexed_ruleset_base_dir_, indexed_version,
+ unindexed_ruleset_path),
+ base::Bind(&RulesetService::OnWrittenRuleset, AsWeakPtr(),
+ indexed_version, success_callback));
+}
// static
-bool RulesetService::WriteRuleset(const base::FilePath& base_dir,
- const RulesetVersion& version,
- std::vector<uint8_t> data) {
+bool RulesetService::IndexAndWriteRuleset(
+ const base::FilePath& indexed_ruleset_base_dir,
+ const IndexedRulesetVersion& indexed_version,
+ const base::FilePath& unindexed_ruleset_path) {
base::ThreadRestrictions::AssertIOAllowed();
+
+ std::string unindexed_ruleset_data;
+ if (!base::ReadFileToString(unindexed_ruleset_path, &unindexed_ruleset_data))
+ return false;
+
+ // TODO(pkalinnikov): Implement streaming wire format here.
+ proto::UrlRule rule;
+ if (!rule.ParseFromString(unindexed_ruleset_data))
+ return false;
+
+ RulesetIndexer indexer;
+ indexer.AddUrlRule(rule);
+ indexer.Finish();
+
+ return WriteRuleset(indexed_ruleset_base_dir, indexed_version, indexer.data(),
+ indexer.size());
+}
+
+// static
+bool RulesetService::WriteRuleset(
+ const base::FilePath& indexed_ruleset_base_dir,
+ const IndexedRulesetVersion& indexed_version,
+ const uint8_t* data,
+ size_t length) {
+ base::FilePath indexed_ruleset_version_dir =
+ indexed_version.GetSubdirectoryPathForVersion(indexed_ruleset_base_dir);
base::ScopedTempDir scratch_dir;
- if (!scratch_dir.CreateUniqueTempDirUnderPath(base_dir))
+ if (!scratch_dir.CreateUniqueTempDirUnderPath(
+ indexed_ruleset_version_dir.DirName())) {
return false;
+ }
static_assert(sizeof(uint8_t) == sizeof(char), "Expected char = byte.");
- const int data_size_in_chars = base::checked_cast<int>(data.size());
+ const int data_size_in_chars = base::checked_cast<int>(length);
if (base::WriteFile(GetRulesetDataFilePath(scratch_dir.path()),
- reinterpret_cast<const char*>(data.data()),
+ reinterpret_cast<const char*>(data),
data_size_in_chars) != data_size_in_chars) {
return false;
}
- DCHECK(base::PathExists(base_dir));
- if (base::ReplaceFile(scratch_dir.path(),
- GetSubdirectoryPathForVersion(base_dir, version),
+ // Creating a temporary directory also makes sure the path (except for the
+ // final segment) gets created. ReplaceFile will not create the path.
+ DCHECK(base::PathExists(indexed_ruleset_version_dir.DirName()));
+
+ // This will attempt to overwrite the previously stored ruleset with the same
+ // version, if any. Doing so is needed in case the earlier write was
+ // interrupted, but will fail on Windows in case the earlier write was
+ // successful and the ruleset is in use. We should not normally get to here in
+ // that case, however, due to the same-version check above. Even if we do, the
+ // worst-case scenario is that a slightly-older ruleset version will be used
+ // until next restart/ruleset update.
+ if (base::ReplaceFile(scratch_dir.path(), indexed_ruleset_version_dir,
nullptr)) {
scratch_dir.Take();
return true;
@@ -171,32 +224,48 @@ bool RulesetService::WriteRuleset(const base::FilePath& base_dir,
return false;
}
-void RulesetService::OnWrittenRuleset(const RulesetVersion& version,
- bool success) {
+void RulesetService::OnWrittenRuleset(
+ const IndexedRulesetVersion& version,
+ const WriteRulesetCallback& result_callback,
+ bool success) {
+ DCHECK(!result_callback.is_null());
if (!success)
return;
-
version.SaveToPrefs(local_state_);
- OpenAndPublishRuleset(version);
+ result_callback.Run(version);
}
-void RulesetService::OpenAndPublishRuleset(const RulesetVersion& version) {
+void RulesetService::OpenAndPublishRuleset(
+ const IndexedRulesetVersion& version) {
ruleset_data_.reset(new base::FileProxy(blocking_task_runner_.get()));
// On Windows, open the file with FLAG_SHARE_DELETE to allow deletion while
// there are handles to it still open. Note that creating a new file at the
- // same path would still be impossible until after the last of those handles
- // is closed.
+ // same path would still be impossible until after the last handle is closed.
ruleset_data_->CreateOrOpen(
- GetRulesetDataFilePath(GetSubdirectoryPathForVersion(base_dir_, version)),
+ GetRulesetDataFilePath(
+ version.GetSubdirectoryPathForVersion(indexed_ruleset_base_dir_)),
base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_SHARE_DELETE,
base::Bind(&RulesetService::OnOpenedRuleset, AsWeakPtr()));
}
void RulesetService::OnOpenedRuleset(base::File::Error error) {
- // This should not happen unless |base_dir| has been tampered with.
- if (!ruleset_data_ || !ruleset_data_->IsValid())
+ // The file has just been successfully written, so a failure here is unlikely
+ // unless |indexed_ruleset_base_dir_| has been tampered with or there are disk
+ // errors. Still, restore the invariant that a valid version in preferences
+ // always points to an existing version of disk by invalidating the prefs.
+ if (error != base::File::FILE_OK) {
+ IndexedRulesetVersion().SaveToPrefs(local_state_);
+ return;
+ }
+
+ // A second call to OpenAndPublishRuleset() may have reset |ruleset_data_| to
+ // a new instance that is in the process of calling CreateOrOpen() on a newer
+ // version. Bail out.
+ DCHECK(ruleset_data_);
+ if (!ruleset_data_->IsValid())
return;
+
DCHECK_EQ(error, base::File::Error::FILE_OK);
for (auto& distributor : distributors_)
distributor->PublishNewVersion(ruleset_data_->DuplicateFile());

Powered by Google App Engine
This is Rietveld 408576698