OLD | NEW |
---|---|
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" | 7 #include "base/files/file_util.h" |
8 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
9 #include "base/metrics/sparse_histogram.h" | 9 #include "base/metrics/sparse_histogram.h" |
10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
47 } | 47 } |
48 | 48 |
49 // Returns the name of the temporary file used to buffer data for | 49 // Returns the name of the temporary file used to buffer data for |
50 // |filename|. Exported for unit tests. | 50 // |filename|. Exported for unit tests. |
51 const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) { | 51 const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) { |
52 return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new")); | 52 return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new")); |
53 } | 53 } |
54 | 54 |
55 } // namespace | 55 } // namespace |
56 | 56 |
57 using ::google::protobuf::RepeatedField; | |
58 using ::google::protobuf::RepeatedPtrField; | |
59 using ::google::protobuf::int32; | |
Nathan Parker
2016/07/15 16:49:38
seems like this might conflict with an existing in
vakh (use Gerrit instead)
2016/07/18 18:43:17
Can't find any definitions: https://cs.chromium.or
| |
60 | |
57 std::ostream& operator<<(std::ostream& os, const V4Store& store) { | 61 std::ostream& operator<<(std::ostream& os, const V4Store& store) { |
58 os << store.DebugString(); | 62 os << store.DebugString(); |
59 return os; | 63 return os; |
60 } | 64 } |
61 | 65 |
62 V4Store* V4StoreFactory::CreateV4Store( | 66 V4Store* V4StoreFactory::CreateV4Store( |
63 const scoped_refptr<base::SequencedTaskRunner>& task_runner, | 67 const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
64 const base::FilePath& store_path) { | 68 const base::FilePath& store_path) { |
65 V4Store* new_store = new V4Store(task_runner, store_path); | 69 V4Store* new_store = new V4Store(task_runner, store_path); |
66 new_store->Initialize(); | 70 new_store->Initialize(); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
103 new V4Store(this->task_runner_, this->store_path_)); | 107 new V4Store(this->task_runner_, this->store_path_)); |
104 new_store->state_ = response->new_client_state(); | 108 new_store->state_ = response->new_client_state(); |
105 // TODO(vakh): | 109 // TODO(vakh): |
106 // 1. Merge the old store and the new update in new_store. | 110 // 1. Merge the old store and the new update in new_store. |
107 // 2. Create a ListUpdateResponse containing RICE encoded hash-prefixes and | 111 // 2. Create a ListUpdateResponse containing RICE encoded hash-prefixes and |
108 // response_type as FULL_UPDATE, and write that to disk. | 112 // response_type as FULL_UPDATE, and write that to disk. |
109 // 3. Remove this if condition after completing 1. and 2. | 113 // 3. Remove this if condition after completing 1. and 2. |
110 HashPrefixMap hash_prefix_map; | 114 HashPrefixMap hash_prefix_map; |
111 ApplyUpdateResult apply_update_result; | 115 ApplyUpdateResult apply_update_result; |
112 if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) { | 116 if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) { |
117 int removals_size = response->removals_size(); | |
Nathan Parker
2016/07/15 16:49:38
no need for the temp removals_size if you're not r
vakh (use Gerrit instead)
2016/07/18 18:43:17
Done.
| |
118 DCHECK(removals_size <= 1); | |
119 RepeatedField<int32> raw_removals; | |
113 for (const auto& removal : response->removals()) { | 120 for (const auto& removal : response->removals()) { |
Nathan Parker
2016/07/15 16:49:38
You could simplify this since you expect 0 or 1 re
vakh (use Gerrit instead)
2016/07/18 18:43:17
Done.
| |
114 // TODO(vakh): Allow other compression types. | 121 // TODO(vakh): Allow other compression types. |
115 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 | 122 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 |
116 DCHECK_EQ(RAW, removal.compression_type()); | 123 DCHECK_EQ(RAW, removal.compression_type()); |
124 const RawIndices& raw_indices = removal.raw_indices(); | |
125 raw_removals = raw_indices.indices(); | |
Nathan Parker
2016/07/15 16:49:38
This does a copy
vakh (use Gerrit instead)
2016/07/18 18:43:17
Done.
| |
117 } | 126 } |
118 | 127 |
119 apply_update_result = UpdateHashPrefixMapFromAdditions( | 128 apply_update_result = UpdateHashPrefixMapFromAdditions( |
120 response->additions(), &hash_prefix_map); | 129 response->additions(), &hash_prefix_map); |
121 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 130 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
122 apply_update_result = | 131 apply_update_result = new_store->MergeUpdate( |
123 new_store->MergeUpdate(hash_prefix_map_, hash_prefix_map); | 132 hash_prefix_map_, hash_prefix_map, raw_removals); |
124 } | 133 } |
125 // TODO(vakh): Generate the updated ListUpdateResponse to write to disk. | 134 // TODO(vakh): Generate the updated ListUpdateResponse to write to disk. |
126 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { | 135 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { |
127 apply_update_result = UpdateHashPrefixMapFromAdditions( | 136 apply_update_result = UpdateHashPrefixMapFromAdditions( |
128 response->additions(), &hash_prefix_map); | 137 response->additions(), &hash_prefix_map); |
129 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 138 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
130 new_store->hash_prefix_map_ = hash_prefix_map; | 139 new_store->hash_prefix_map_ = hash_prefix_map; |
131 RecordStoreWriteResult(new_store->WriteToDisk(std::move(response))); | 140 RecordStoreWriteResult(new_store->WriteToDisk(std::move(response))); |
132 } | 141 } |
133 } else { | 142 } else { |
134 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; | 143 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; |
135 NOTREACHED() << "Unexpected response type: " << response->response_type(); | 144 NOTREACHED() << "Unexpected response type: " << response->response_type(); |
136 } | 145 } |
137 | 146 |
138 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 147 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
139 // new_store is done updating, pass it to the callback. | 148 // new_store is done updating, pass it to the callback. |
140 callback_task_runner->PostTask( | 149 callback_task_runner->PostTask( |
141 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); | 150 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); |
142 } else { | 151 } else { |
143 // new_store failed updating. Pass a nullptr to the callback. | 152 // new_store failed updating. Pass a nullptr to the callback. |
144 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); | 153 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
145 } | 154 } |
146 | 155 |
147 RecordApplyUpdateResult(apply_update_result); | 156 RecordApplyUpdateResult(apply_update_result); |
148 } | 157 } |
149 | 158 |
150 // static | 159 // static |
151 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( | 160 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( |
152 const ::google::protobuf::RepeatedPtrField<ThreatEntrySet>& additions, | 161 const RepeatedPtrField<ThreatEntrySet>& additions, |
153 HashPrefixMap* additions_map) { | 162 HashPrefixMap* additions_map) { |
154 for (const auto& addition : additions) { | 163 for (const auto& addition : additions) { |
155 // TODO(vakh): Allow other compression types. | 164 // TODO(vakh): Allow other compression types. |
156 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 | 165 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 |
157 DCHECK_EQ(RAW, addition.compression_type()); | 166 DCHECK_EQ(RAW, addition.compression_type()); |
158 | 167 |
159 DCHECK(addition.has_raw_hashes()); | 168 DCHECK(addition.has_raw_hashes()); |
160 DCHECK(addition.raw_hashes().has_raw_hashes()); | 169 DCHECK(addition.raw_hashes().has_raw_hashes()); |
161 | 170 |
162 PrefixSize prefix_size = addition.raw_hashes().prefix_size(); | 171 PrefixSize prefix_size = addition.raw_hashes().prefix_size(); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
232 | 241 |
233 const HashPrefixes& existing_prefixes = | 242 const HashPrefixes& existing_prefixes = |
234 (*prefix_map_to_update)[prefix_size]; | 243 (*prefix_map_to_update)[prefix_size]; |
235 size_t existing_capacity = existing_prefixes.capacity(); | 244 size_t existing_capacity = existing_prefixes.capacity(); |
236 | 245 |
237 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + | 246 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + |
238 prefix_length_to_add); | 247 prefix_length_to_add); |
239 } | 248 } |
240 } | 249 } |
241 | 250 |
242 ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map, | 251 ApplyUpdateResult V4Store::MergeUpdate( |
243 const HashPrefixMap& additions_map) { | 252 const HashPrefixMap& old_prefixes_map, |
253 const HashPrefixMap& additions_map, | |
254 const RepeatedField<int32>& raw_removals) { | |
255 const int* removals_iter = raw_removals.begin(); | |
Nathan Parker
2016/07/15 16:49:38
Put this down by total_picked_from_old, since you
vakh (use Gerrit instead)
2016/07/18 18:43:17
Done.
| |
256 | |
244 DCHECK(hash_prefix_map_.empty()); | 257 DCHECK(hash_prefix_map_.empty()); |
245 hash_prefix_map_.clear(); | 258 hash_prefix_map_.clear(); |
246 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); | 259 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); |
247 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); | 260 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); |
248 | 261 |
249 IteratorMap old_iterator_map; | 262 IteratorMap old_iterator_map; |
250 HashPrefix next_smallest_prefix_old; | 263 HashPrefix next_smallest_prefix_old; |
251 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); | 264 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); |
252 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( | 265 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( |
253 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 266 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
254 | 267 |
255 IteratorMap additions_iterator_map; | 268 IteratorMap additions_iterator_map; |
256 HashPrefix next_smallest_prefix_additions; | 269 HashPrefix next_smallest_prefix_additions; |
257 InitializeIteratorMap(additions_map, &additions_iterator_map); | 270 InitializeIteratorMap(additions_map, &additions_iterator_map); |
258 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( | 271 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( |
259 additions_map, additions_iterator_map, &next_smallest_prefix_additions); | 272 additions_map, additions_iterator_map, &next_smallest_prefix_additions); |
260 | 273 |
261 // Classical merge sort. | 274 // Classical merge sort. |
262 // The two constructs to merge are maps: old_prefixes_map, additions_map. | 275 // The two constructs to merge are maps: old_prefixes_map, additions_map. |
263 // At least one of the maps still has elements that need to be merged into the | 276 // At least one of the maps still has elements that need to be merged into the |
264 // new store. | 277 // new store. |
278 | |
279 // Keep track of the number of elements picked from the old map. This is used | |
280 // to determine which elements to drop based on the raw_removals. Note that | |
281 // picked is not the same as merged. A picked element isn't merged if its | |
282 // index is on the raw_removals list. | |
283 int total_picked_from_old = 0; | |
265 while (old_has_unmerged || additions_has_unmerged) { | 284 while (old_has_unmerged || additions_has_unmerged) { |
266 // If the same hash prefix appears in the existing store and the additions | 285 // If the same hash prefix appears in the existing store and the additions |
267 // list, something is clearly wrong. Discard the update. | 286 // list, something is clearly wrong. Discard the update. |
268 if (old_has_unmerged && additions_has_unmerged && | 287 if (old_has_unmerged && additions_has_unmerged && |
269 next_smallest_prefix_old == next_smallest_prefix_additions) { | 288 next_smallest_prefix_old == next_smallest_prefix_additions) { |
270 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; | 289 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; |
271 } | 290 } |
272 | 291 |
273 // Select which map to pick the next hash prefix from to keep the result in | 292 // Select which map to pick the next hash prefix from to keep the result in |
274 // lexographically sorted order. | 293 // lexographically sorted order. |
275 bool pick_from_old = | 294 bool pick_from_old = |
276 old_has_unmerged && | 295 old_has_unmerged && |
277 (!additions_has_unmerged || | 296 (!additions_has_unmerged || |
278 (next_smallest_prefix_old < next_smallest_prefix_additions)); | 297 (next_smallest_prefix_old < next_smallest_prefix_additions)); |
279 | 298 |
280 PrefixSize next_smallest_prefix_size; | 299 PrefixSize next_smallest_prefix_size; |
281 if (pick_from_old) { | 300 if (pick_from_old) { |
282 next_smallest_prefix_size = next_smallest_prefix_old.size(); | 301 next_smallest_prefix_size = next_smallest_prefix_old.size(); |
283 | 302 |
284 // Append the smallest hash to the appropriate list. | |
285 hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old; | |
286 | |
287 // Update the iterator map, which means that we have merged one hash | 303 // Update the iterator map, which means that we have merged one hash |
288 // prefix of size |next_size_for_old| from the old store. | 304 // prefix of size |next_size_for_old| from the old store. |
289 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; | 305 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; |
290 | 306 |
307 if (removals_iter == raw_removals.end() || | |
308 *removals_iter != total_picked_from_old) { | |
309 // Append the smallest hash to the appropriate list. | |
310 hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old; | |
311 } else { | |
312 // Element not added to new map. Move the removals iterator forward. | |
313 removals_iter++; | |
314 } | |
315 | |
316 total_picked_from_old++; | |
317 | |
291 // Find the next smallest unmerged element in the old store's map. | 318 // Find the next smallest unmerged element in the old store's map. |
292 old_has_unmerged = GetNextSmallestUnmergedPrefix( | 319 old_has_unmerged = GetNextSmallestUnmergedPrefix( |
293 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 320 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
294 } else { | 321 } else { |
295 next_smallest_prefix_size = next_smallest_prefix_additions.size(); | 322 next_smallest_prefix_size = next_smallest_prefix_additions.size(); |
296 | 323 |
297 // Append the smallest hash to the appropriate list. | 324 // Append the smallest hash to the appropriate list. |
298 hash_prefix_map_[next_smallest_prefix_size] += | 325 hash_prefix_map_[next_smallest_prefix_size] += |
299 next_smallest_prefix_additions; | 326 next_smallest_prefix_additions; |
300 | 327 |
301 // Update the iterator map, which means that we have merged one hash | 328 // Update the iterator map, which means that we have merged one hash |
302 // prefix of size |next_smallest_prefix_size| from the update. | 329 // prefix of size |next_smallest_prefix_size| from the update. |
303 additions_iterator_map[next_smallest_prefix_size] += | 330 additions_iterator_map[next_smallest_prefix_size] += |
304 next_smallest_prefix_size; | 331 next_smallest_prefix_size; |
305 | 332 |
306 // Find the next smallest unmerged element in the additions map. | 333 // Find the next smallest unmerged element in the additions map. |
307 additions_has_unmerged = | 334 additions_has_unmerged = |
308 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, | 335 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, |
309 &next_smallest_prefix_additions); | 336 &next_smallest_prefix_additions); |
310 } | 337 } |
311 } | 338 } |
312 | 339 |
313 return APPLY_UPDATE_SUCCESS; | 340 return (removals_iter == raw_removals.end()) ? APPLY_UPDATE_SUCCESS |
341 : REMOVALS_INDEX_TOO_LARGE; | |
314 } | 342 } |
315 | 343 |
316 StoreReadResult V4Store::ReadFromDisk() { | 344 StoreReadResult V4Store::ReadFromDisk() { |
317 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 345 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
318 | 346 |
319 std::string contents; | 347 std::string contents; |
320 bool read_success = base::ReadFileToString(store_path_, &contents); | 348 bool read_success = base::ReadFileToString(store_path_, &contents); |
321 if (!read_success) { | 349 if (!read_success) { |
322 return FILE_UNREADABLE_FAILURE; | 350 return FILE_UNREADABLE_FAILURE; |
323 } | 351 } |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 | 420 |
393 if (!base::Move(new_filename, store_path_)) { | 421 if (!base::Move(new_filename, store_path_)) { |
394 DVLOG(1) << "store_path_: " << store_path_.value(); | 422 DVLOG(1) << "store_path_: " << store_path_.value(); |
395 return UNABLE_TO_RENAME_FAILURE; | 423 return UNABLE_TO_RENAME_FAILURE; |
396 } | 424 } |
397 | 425 |
398 return WRITE_SUCCESS; | 426 return WRITE_SUCCESS; |
399 } | 427 } |
400 | 428 |
401 } // namespace safe_browsing | 429 } // namespace safe_browsing |
OLD | NEW |