| Index: components/safe_browsing_db/v4_store.cc
|
| diff --git a/components/safe_browsing_db/v4_store.cc b/components/safe_browsing_db/v4_store.cc
|
| index 9db2ab37f10ffab0067ae231fa44f00fabdd2f5c..a486c7f630eee4327ea55903fec6fd4e70dad588 100644
|
| --- a/components/safe_browsing_db/v4_store.cc
|
| +++ b/components/safe_browsing_db/v4_store.cc
|
| @@ -4,11 +4,38 @@
|
|
|
| #include "base/base64.h"
|
| #include "base/bind.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| +#include "base/metrics/sparse_histogram.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "components/safe_browsing_db/v4_store.h"
|
| +#include "components/safe_browsing_db/v4_store.pb.h"
|
|
|
| namespace safe_browsing {
|
|
|
| +namespace {
|
| +const uint32_t kFileMagic = 0x600D71FE;
|
| +
|
| +const uint32_t kFileVersion = 9;
|
| +
|
| +void RecordStoreReadResult(StoreReadResult result) {
|
| + UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreReadResult", result,
|
| + STORE_READ_RESULT_MAX);
|
| +}
|
| +
|
| +void RecordStoreWriteResult(StoreWriteResult result) {
|
| + UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreWriteResult", result,
|
| + STORE_WRITE_RESULT_MAX);
|
| +}
|
| +
|
| +// Returns the name of the temporary file used to buffer data for
|
| +// |filename|. Exported for unit tests.
|
| +const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) {
|
| + return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new"));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| std::ostream& operator<<(std::ostream& os, const V4Store& store) {
|
| os << store.DebugString();
|
| return os;
|
| @@ -17,7 +44,17 @@ std::ostream& operator<<(std::ostream& os, const V4Store& store) {
|
| V4Store* V4StoreFactory::CreateV4Store(
|
| const scoped_refptr<base::SequencedTaskRunner>& task_runner,
|
| const base::FilePath& store_path) {
|
| - return new V4Store(task_runner, store_path);
|
| + V4Store* new_store = new V4Store(task_runner, store_path);
|
| + new_store->Initialize();
|
| + return new_store;
|
| +}
|
| +
|
| +void V4Store::Initialize() {
|
| + // If a state already exists, don't re-initilize.
|
| + DCHECK(state_.empty());
|
| +
|
| + StoreReadResult store_read_result = ReadFromDisk();
|
| + RecordStoreReadResult(store_read_result);
|
| }
|
|
|
| V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
|
| @@ -50,9 +87,94 @@ void V4Store::ApplyUpdate(
|
| // TODO(vakh): The new store currently only picks up the new state. Do more.
|
| new_store->state_ = response.new_client_state();
|
|
|
| + // TODO(vakh): Merge the old store and the new update in new_store.
|
| + // Then, create a ListUpdateResponse containing RICE encoded hash-prefixes and
|
| + // response_type as FULL_UPDATE, and write that to disk.
|
| + StoreWriteResult result = new_store->WriteToDisk(response);
|
| + RecordStoreWriteResult(result);
|
| +
|
| // new_store is done updating, pass it to the callback.
|
| callback_task_runner->PostTask(
|
| FROM_HERE, base::Bind(callback, base::Passed(&new_store)));
|
| }
|
|
|
| +StoreReadResult V4Store::ReadFromDisk() {
|
| + DCHECK(task_runner_->RunsTasksOnCurrentThread());
|
| +
|
| + std::string contents;
|
| + bool read_success = base::ReadFileToString(store_path_, &contents);
|
| + if (!read_success) {
|
| + return FILE_UNREADABLE_FAILURE;
|
| + }
|
| +
|
| + if (contents.empty()) {
|
| + return FILE_EMPTY_FAILURE;
|
| + }
|
| +
|
| + V4StoreFileFormat file_format;
|
| + if (!file_format.ParseFromString(contents)) {
|
| + return PROTO_PARSING_FAILURE;
|
| + }
|
| +
|
| + if (file_format.magic_number() != kFileMagic) {
|
| + DVLOG(1) << "Unexpected magic number found in file: "
|
| + << file_format.magic_number();
|
| + return UNEXPECTED_MAGIC_NUMBER_FAILURE;
|
| + }
|
| +
|
| + UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.V4StoreVersionRead",
|
| + file_format.version_number());
|
| + if (file_format.version_number() != kFileVersion) {
|
| + DVLOG(1) << "File version incompatible: " << file_format.version_number()
|
| + << "; expected: " << kFileVersion;
|
| + return FILE_VERSION_INCOMPATIBLE_FAILURE;
|
| + }
|
| +
|
| + if (!file_format.has_list_update_response()) {
|
| + return HASH_PREFIX_INFO_MISSING_FAILURE;
|
| + }
|
| +
|
| + ListUpdateResponse list_update_response = file_format.list_update_response();
|
| + state_ = list_update_response.new_client_state();
|
| + // TODO(vakh): Do more with what's read from the disk.
|
| +
|
| + return READ_SUCCESS;
|
| +}
|
| +
|
| +StoreWriteResult V4Store::WriteToDisk(
|
| + const ListUpdateResponse& response) const {
|
| + // Do not write partial updates to the disk.
|
| + // After merging the updates, the ListUpdateResponse passed to this method
|
| + // should be a FULL_UPDATE.
|
| + if (!response.has_response_type() ||
|
| + response.response_type() != ListUpdateResponse::FULL_UPDATE) {
|
| + DVLOG(1) << "response.has_response_type(): "
|
| + << response.has_response_type();
|
| + DVLOG(1) << "response.response_type(): " << response.response_type();
|
| + return INVALID_RESPONSE_TYPE_FAILURE;
|
| + }
|
| +
|
| + // Attempt writing to a temporary file first and at the end, swap the files.
|
| + const base::FilePath new_filename = TemporaryFileForFilename(store_path_);
|
| +
|
| + V4StoreFileFormat file_format;
|
| + file_format.set_magic_number(kFileMagic);
|
| + file_format.set_version_number(kFileVersion);
|
| + ListUpdateResponse* response_to_write =
|
| + file_format.mutable_list_update_response();
|
| + *response_to_write = response;
|
| + std::string file_format_string;
|
| + file_format.SerializeToString(&file_format_string);
|
| + size_t written = base::WriteFile(new_filename, file_format_string.data(),
|
| + file_format_string.size());
|
| + DCHECK_EQ(file_format_string.size(), written);
|
| +
|
| + if (!base::Move(new_filename, store_path_)) {
|
| + DVLOG(1) << "store_path_: " << store_path_.value();
|
| + return UNABLE_TO_RENAME_FAILURE;
|
| + }
|
| +
|
| + return WRITE_SUCCESS;
|
| +}
|
| +
|
| } // namespace safe_browsing
|
|
|