Chromium Code Reviews| 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..1123f8826527253e8d97ec85a8e89485be352b41 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.h" |
|
rkaplow
2016/06/27 17:48:51
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 |