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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/base64.h" 5 #include "base/base64.h"
6 #include "base/bind.h" 6 #include "base/bind.h"
7 #include "base/files/file_util.h"
8 #include "base/metrics/histogram.h"
9 #include "base/metrics/sparse_histogram.h"
7 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
8 #include "components/safe_browsing_db/v4_store.h" 11 #include "components/safe_browsing_db/v4_store.h"
12 #include "components/safe_browsing_db/v4_store.pb.h"
9 13
10 namespace safe_browsing { 14 namespace safe_browsing {
11 15
16 namespace {
17 // NOTE(vakh): kFileMagic should not be a byte-wise palindrome, so
18 // 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.
19 const uint32_t kFileMagic = 0x600D71FE;
20
21 const uint32_t kFileVersion = 9;
22
23 void RecordStoreReadResult(StoreReadResult result) {
24 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreReadResult", result,
25 STORE_READ_RESULT_MAX);
26 }
27
28 void RecordStoreWriteResult(StoreWriteResult result) {
29 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreWriteResult", result,
30 STORE_WRITE_RESULT_MAX);
31 }
32
33 // Returns the name of the temporary file used to buffer data for
34 // |filename|. Exported for unit tests.
35 const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) {
36 return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new"));
37 }
38
39 } // namespace
40
12 std::ostream& operator<<(std::ostream& os, const V4Store& store) { 41 std::ostream& operator<<(std::ostream& os, const V4Store& store) {
13 os << store.DebugString(); 42 os << store.DebugString();
14 return os; 43 return os;
15 } 44 }
16 45
17 V4Store* V4StoreFactory::CreateV4Store( 46 V4Store* V4StoreFactory::CreateV4Store(
18 const scoped_refptr<base::SequencedTaskRunner>& task_runner, 47 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
19 const base::FilePath& store_path) { 48 const base::FilePath& store_path) {
20 return new V4Store(task_runner, store_path); 49 V4Store* new_store = new V4Store(task_runner, store_path);
50 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
51 return new_store;
52 }
53
54 void V4Store::Initialize() {
55 // 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.
56 DCHECK(state_.empty());
57
58 StoreReadResult store_read_result = ReadFromDisk();
59 RecordStoreReadResult(store_read_result);
21 } 60 }
22 61
23 V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner, 62 V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
24 const base::FilePath& store_path) 63 const base::FilePath& store_path)
25 : store_path_(store_path), task_runner_(task_runner) {} 64 : store_path_(store_path), task_runner_(task_runner) {}
26 65
27 V4Store::~V4Store() {} 66 V4Store::~V4Store() {}
28 67
29 std::string V4Store::DebugString() const { 68 std::string V4Store::DebugString() const {
30 std::string state_base64; 69 std::string state_base64;
31 base::Base64Encode(state_, &state_base64); 70 base::Base64Encode(state_, &state_base64);
32 71
33 std::string value; 72 std::string value;
34 return base::SStringPrintf(&value, "path: %s; state: %s", 73 return base::SStringPrintf(&value, "{path: %s; state: %s}",
35 store_path_.value().c_str(), state_base64.c_str()); 74 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.
36 } 75 }
37 76
38 bool V4Store::Reset() { 77 bool V4Store::Reset() {
39 // TODO(vakh): Implement skeleton. 78 // TODO(vakh): Implement skeleton.
40 state_ = ""; 79 state_ = "";
41 return true; 80 return true;
42 } 81 }
43 82
44 void V4Store::ApplyUpdate( 83 void V4Store::ApplyUpdate(
45 const ListUpdateResponse& response, 84 const ListUpdateResponse& response,
46 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, 85 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
47 UpdatedStoreReadyCallback callback) { 86 UpdatedStoreReadyCallback callback) {
48 std::unique_ptr<V4Store> new_store( 87 std::unique_ptr<V4Store> new_store(
49 new V4Store(this->task_runner_, this->store_path_)); 88 new V4Store(this->task_runner_, this->store_path_));
50 89
51 // TODO(vakh): The new store currently only picks up the new state. Do more. 90 // TODO(vakh): The new store currently only picks up the new state. Do more.
52 new_store->state_ = response.new_client_state(); 91 new_store->state_ = response.new_client_state();
53 92
93 // TODO(vakh): Merge the old store and the new update in new_store.
94 // Then, create a ListUpdateResponse containing RICE encoded hash-prefixes and
95 // response_type as FULL_UPDATE, and write that to disk.
96 StoreWriteResult result = new_store->WriteToDisk(response);
97 RecordStoreWriteResult(result);
98
54 // new_store is done updating, pass it to the callback. 99 // new_store is done updating, pass it to the callback.
55 callback_task_runner->PostTask( 100 callback_task_runner->PostTask(
56 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); 101 FROM_HERE, base::Bind(callback, base::Passed(&new_store)));
57 } 102 }
58 103
104 StoreReadResult V4Store::ReadFromDisk() {
105 DCHECK(task_runner_->RunsTasksOnCurrentThread());
106
107 std::string contents;
108 bool read_success = base::ReadFileToString(store_path_, &contents);
109 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.
110 if (!read_success) {
111 return FILE_UNREADABLE_FAILURE;
112 }
113
114 if (contents.empty()) {
115 return FILE_EMPTY_FAILURE;
116 }
117
118 if (!file_format.ParseFromString(contents)) {
119 return PROTO_PARSING_FAILURE;
120 }
121
122 if (!file_format.has_magic_number() ||
123 file_format.magic_number() != kFileMagic) {
124 DVLOG(1) << "Unexpected magic number found in file: "
125 << 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.
126 return UNEXPECTED_MAGIC_NUMBER_FAILURE;
127 }
128
129 UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.V4StoreVersionRead",
130 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
131 if (!file_format.has_version_number() ||
132 file_format.version_number() != kFileVersion) {
133 DVLOG(1) << "File version incompatible: " << file_format.version_number()
134 << "; 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.
135 return FILE_VERSION_INCOMPATIBLE_FAILURE;
136 }
137
138 if (!file_format.has_list_update_response()) {
139 return HASH_PREFIX_INFO_MISSING_FAILURE;
140 }
141
142 ListUpdateResponse list_update_response = file_format.list_update_response();
143 state_ = list_update_response.new_client_state();
144 // TODO(vakh): Do more with what's read from the disk.
145
146 return READ_SUCCESS;
147 }
148
149 StoreWriteResult V4Store::WriteToDisk(
150 const ListUpdateResponse& response) const {
151 // Do not write partial updates to the disk.
152 // After merging the updates, the ListUpdateResponse passed to this method
153 // should be a FULL_UPDATE.
154 if (!response.has_response_type() ||
155 response.response_type() != ListUpdateResponse::FULL_UPDATE) {
156 DVLOG(1) << "response.has_response_type(): "
157 << response.has_response_type();
158 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.
159 return INVALID_RESPONSE_TYPE_FAILURE;
160 }
161
162 // Attempt writing to a temporary file first and at the end, swap the files.
163 const base::FilePath new_filename = TemporaryFileForFilename(store_path_);
164
165 V4StoreFileFormat file_format;
166 file_format.set_magic_number(kFileMagic);
167 file_format.set_version_number(kFileVersion);
168 ListUpdateResponse* response_to_write =
169 file_format.mutable_list_update_response();
170 *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.
171 std::string file_format_string;
172 file_format.SerializeToString(&file_format_string);
173 size_t written = base::WriteFile(new_filename, file_format_string.c_str(),
174 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.
175 if (file_format_string.size() != written) {
176 DVLOG(1) << "file_format_string.size(): " << file_format_string.size();
177 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.
178 return UNEXPECTED_BYTES_WRITTEN_FAILURE;
179 }
180
181 if (!base::Move(new_filename, store_path_)) {
182 DVLOG(1) << "store_path_: " << store_path_.value();
183 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.
184 return UNABLE_TO_RENAME_FAILURE;
185 }
186
187 return WRITE_SUCCESS;
188 }
189
59 } // namespace safe_browsing 190 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698