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

Unified Diff: components/safe_browsing_db/v4_store.cc

Issue 2066083002: SafeBrowising: Read and write V4Store from/to disk (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@01_UpdateDbAndStores
Patch Set: Track writes to store via UMA. Create a temp file for writing. Remove some DVLOGs. Created 4 years, 6 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/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 c32f42009865f756bb3d42077c431aaa8490d655..bcceda49bc09d7a66a82e6f428fb762f6a1e5185 100644
--- a/components/safe_browsing_db/v4_store.cc
+++ b/components/safe_browsing_db/v4_store.cc
@@ -4,11 +4,40 @@
#include "base/base64.h"
#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/metrics/histogram.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 {
+// NOTE(vakh): kFileMagic should not be a byte-wise palindrome, so
+// that byte-order changes force corruption.
Scott Hess - ex-Googler 2016/06/22 22:58:38 Protobuf would handle byte-ordering issues, so thi
vakh (use Gerrit instead) 2016/06/23 23:09:31 Removed the comment.
+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 +46,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();
Scott Hess - ex-Googler 2016/06/22 22:58:39 If this is only called from the factory, it might
vakh (use Gerrit instead) 2016/06/23 23:09:32 FakeV4StoreFactory, derived from V4StoreFactory, a
Scott Hess - ex-Googler 2016/06/24 04:14:11 Acknowledged. But it kind of feels like your inhe
+ return new_store;
+}
+
+void V4Store::Initialize() {
+ // If a state already exists, don't re-initilize.
vakh (use Gerrit instead) 2016/06/22 07:28:04 Nit: remove this comment.
Scott Hess - ex-Googler 2016/06/22 22:58:38 I'm fine with a comment, but the comment makes it
vakh (use Gerrit instead) 2016/06/24 18:03:50 Acknowledged.
+ DCHECK(state_.empty());
+
+ StoreReadResult store_read_result = ReadFromDisk();
+ RecordStoreReadResult(store_read_result);
}
V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
@@ -31,7 +70,7 @@ std::string V4Store::DebugString() const {
base::Base64Encode(state_, &state_base64);
std::string value;
- return base::SStringPrintf(&value, "path: %s; state: %s",
+ return base::SStringPrintf(&value, "{path: %s; state: %s}",
store_path_.value().c_str(), state_base64.c_str());
Scott Hess - ex-Googler 2016/06/22 22:58:38 Thanks! This can be base::StringPrintf() without
vakh (use Gerrit instead) 2016/06/23 23:09:32 Done.
}
@@ -51,9 +90,101 @@ 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);
+ V4StoreFileFormat file_format;
Scott Hess - ex-Googler 2016/06/22 22:58:39 Push file_format down to where it's used.
vakh (use Gerrit instead) 2016/06/23 23:09:31 Done.
+ if (!read_success) {
+ return FILE_UNREADABLE_FAILURE;
+ }
+
+ if (contents.empty()) {
+ return FILE_EMPTY_FAILURE;
+ }
+
+ if (!file_format.ParseFromString(contents)) {
+ return PROTO_PARSING_FAILURE;
+ }
+
+ if (!file_format.has_magic_number() ||
+ file_format.magic_number() != kFileMagic) {
+ DVLOG(1) << "Unexpected magic number found in file: "
+ << file_format.magic_number();
Scott Hess - ex-Googler 2016/06/22 22:58:38 What does this DVLOG do for !has_magic_number()?
vakh (use Gerrit instead) 2016/06/23 23:09:31 You probably meant something else, but if you aski
Scott Hess - ex-Googler 2016/06/24 04:14:11 Mostly I meant that this case is both !has_magic_n
vakh (use Gerrit instead) 2016/06/24 18:03:50 Done.
+ return UNEXPECTED_MAGIC_NUMBER_FAILURE;
+ }
+
+ UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.V4StoreVersionRead",
+ file_format.version_number());
Scott Hess - ex-Googler 2016/06/22 22:58:38 Should probably check if you have a version number
vakh (use Gerrit instead) 2016/06/23 23:09:31 Yes, it returns 0 if it isn't set. We never expect
+ if (!file_format.has_version_number() ||
+ file_format.version_number() != kFileVersion) {
+ DVLOG(1) << "File version incompatible: " << file_format.version_number()
+ << "; expected: " << kFileVersion;
Scott Hess - ex-Googler 2016/06/22 22:58:38 Maybe this should be a DCHECK, I can't think of an
vakh (use Gerrit instead) 2016/06/23 23:09:31 DCHECKs make it hard to test conditions. I feel te
Scott Hess - ex-Googler 2016/06/24 04:14:11 Acknowledged.
+ 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();
Scott Hess - ex-Googler 2016/06/22 22:58:39 I think just going with a DCHECK would be fair for
vakh (use Gerrit instead) 2016/06/23 23:09:31 DCHECK vs tests, as above.
+ 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;
Scott Hess - ex-Googler 2016/06/22 22:58:38 Is there no set_list_update_response() method?
vakh (use Gerrit instead) 2016/06/23 23:09:31 No, it is a repeated field so no set_X only add_X
Scott Hess - ex-Googler 2016/06/24 04:14:11 Acknowledged.
+ std::string file_format_string;
+ file_format.SerializeToString(&file_format_string);
+ size_t written = base::WriteFile(new_filename, file_format_string.c_str(),
+ file_format_string.size());
Scott Hess - ex-Googler 2016/06/22 22:58:38 file_format_string.c_str() has to nul-terminate.
vakh (use Gerrit instead) 2016/06/23 23:09:32 I didn't know about data(), but it looks like it's
Scott Hess - ex-Googler 2016/06/24 04:14:11 Sigh. This changed between C++98 and C++11. I th
vakh (use Gerrit instead) 2016/06/24 18:03:50 Done.
+ if (file_format_string.size() != written) {
+ DVLOG(1) << "file_format_string.size(): " << file_format_string.size();
+ DVLOG(1) << "written: " << written;
Scott Hess - ex-Googler 2016/06/22 22:58:39 DCHECK_EQ() would seem reasonable, this should nev
vakh (use Gerrit instead) 2016/06/23 23:09:31 Done.
+ return UNEXPECTED_BYTES_WRITTEN_FAILURE;
+ }
+
+ if (!base::Move(new_filename, store_path_)) {
+ DVLOG(1) << "store_path_: " << store_path_.value();
+ DVLOG(1) << "new_filename: " << new_filename.value();
Scott Hess - ex-Googler 2016/06/22 22:58:38 new_filename can be derived (or store_path_ can be
vakh (use Gerrit instead) 2016/06/23 23:09:31 Done.
+ return UNABLE_TO_RENAME_FAILURE;
+ }
+
+ return WRITE_SUCCESS;
+}
+
} // namespace safe_browsing

Powered by Google App Engine
This is Rietveld 408576698