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; | |
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 DCHECK(response->removals_size() <= 1); | |
118 const RepeatedField<int32>* raw_removals = &empty_removals_; | |
Nathan Parker
2016/07/18 19:54:35
Does MergeUpdate() need this ptr beyond the lifeti
vakh (use Gerrit instead)
2016/07/18 19:58:30
Done.
| |
113 for (const auto& removal : response->removals()) { | 119 for (const auto& removal : response->removals()) { |
114 // TODO(vakh): Allow other compression types. | 120 // TODO(vakh): Allow other compression types. |
115 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 | 121 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 |
116 DCHECK_EQ(RAW, removal.compression_type()); | 122 DCHECK_EQ(RAW, removal.compression_type()); |
123 raw_removals = &removal.raw_indices().indices(); | |
117 } | 124 } |
118 | 125 |
119 apply_update_result = UpdateHashPrefixMapFromAdditions( | 126 apply_update_result = UpdateHashPrefixMapFromAdditions( |
120 response->additions(), &hash_prefix_map); | 127 response->additions(), &hash_prefix_map); |
121 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 128 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
122 apply_update_result = | 129 apply_update_result = new_store->MergeUpdate( |
123 new_store->MergeUpdate(hash_prefix_map_, hash_prefix_map); | 130 hash_prefix_map_, hash_prefix_map, raw_removals); |
124 } | 131 } |
125 // TODO(vakh): Generate the updated ListUpdateResponse to write to disk. | 132 // TODO(vakh): Generate the updated ListUpdateResponse to write to disk. |
126 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { | 133 } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) { |
127 apply_update_result = UpdateHashPrefixMapFromAdditions( | 134 apply_update_result = UpdateHashPrefixMapFromAdditions( |
128 response->additions(), &hash_prefix_map); | 135 response->additions(), &hash_prefix_map); |
129 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 136 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
130 new_store->hash_prefix_map_ = hash_prefix_map; | 137 new_store->hash_prefix_map_ = hash_prefix_map; |
131 RecordStoreWriteResult(new_store->WriteToDisk(std::move(response))); | 138 RecordStoreWriteResult(new_store->WriteToDisk(std::move(response))); |
132 } | 139 } |
133 } else { | 140 } else { |
134 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; | 141 apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE; |
135 NOTREACHED() << "Unexpected response type: " << response->response_type(); | 142 NOTREACHED() << "Unexpected response type: " << response->response_type(); |
136 } | 143 } |
137 | 144 |
138 if (apply_update_result == APPLY_UPDATE_SUCCESS) { | 145 if (apply_update_result == APPLY_UPDATE_SUCCESS) { |
139 // new_store is done updating, pass it to the callback. | 146 // new_store is done updating, pass it to the callback. |
140 callback_task_runner->PostTask( | 147 callback_task_runner->PostTask( |
141 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); | 148 FROM_HERE, base::Bind(callback, base::Passed(&new_store))); |
142 } else { | 149 } else { |
143 // new_store failed updating. Pass a nullptr to the callback. | 150 // new_store failed updating. Pass a nullptr to the callback. |
144 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); | 151 callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
145 } | 152 } |
146 | 153 |
147 RecordApplyUpdateResult(apply_update_result); | 154 RecordApplyUpdateResult(apply_update_result); |
148 } | 155 } |
149 | 156 |
150 // static | 157 // static |
151 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( | 158 ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions( |
152 const ::google::protobuf::RepeatedPtrField<ThreatEntrySet>& additions, | 159 const RepeatedPtrField<ThreatEntrySet>& additions, |
153 HashPrefixMap* additions_map) { | 160 HashPrefixMap* additions_map) { |
154 for (const auto& addition : additions) { | 161 for (const auto& addition : additions) { |
155 // TODO(vakh): Allow other compression types. | 162 // TODO(vakh): Allow other compression types. |
156 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 | 163 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 |
157 DCHECK_EQ(RAW, addition.compression_type()); | 164 DCHECK_EQ(RAW, addition.compression_type()); |
158 | 165 |
159 DCHECK(addition.has_raw_hashes()); | 166 DCHECK(addition.has_raw_hashes()); |
160 DCHECK(addition.raw_hashes().has_raw_hashes()); | 167 DCHECK(addition.raw_hashes().has_raw_hashes()); |
161 | 168 |
162 PrefixSize prefix_size = addition.raw_hashes().prefix_size(); | 169 PrefixSize prefix_size = addition.raw_hashes().prefix_size(); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 | 238 |
232 const HashPrefixes& existing_prefixes = | 239 const HashPrefixes& existing_prefixes = |
233 (*prefix_map_to_update)[prefix_size]; | 240 (*prefix_map_to_update)[prefix_size]; |
234 size_t existing_capacity = existing_prefixes.capacity(); | 241 size_t existing_capacity = existing_prefixes.capacity(); |
235 | 242 |
236 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + | 243 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + |
237 prefix_length_to_add); | 244 prefix_length_to_add); |
238 } | 245 } |
239 } | 246 } |
240 | 247 |
241 ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map, | 248 ApplyUpdateResult V4Store::MergeUpdate( |
242 const HashPrefixMap& additions_map) { | 249 const HashPrefixMap& old_prefixes_map, |
250 const HashPrefixMap& additions_map, | |
251 const RepeatedField<int32>* raw_removals) { | |
243 DCHECK(hash_prefix_map_.empty()); | 252 DCHECK(hash_prefix_map_.empty()); |
244 hash_prefix_map_.clear(); | 253 hash_prefix_map_.clear(); |
245 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); | 254 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); |
246 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); | 255 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); |
247 | 256 |
248 IteratorMap old_iterator_map; | 257 IteratorMap old_iterator_map; |
249 HashPrefix next_smallest_prefix_old; | 258 HashPrefix next_smallest_prefix_old; |
250 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); | 259 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); |
251 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( | 260 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( |
252 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 261 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
253 | 262 |
254 IteratorMap additions_iterator_map; | 263 IteratorMap additions_iterator_map; |
255 HashPrefix next_smallest_prefix_additions; | 264 HashPrefix next_smallest_prefix_additions; |
256 InitializeIteratorMap(additions_map, &additions_iterator_map); | 265 InitializeIteratorMap(additions_map, &additions_iterator_map); |
257 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( | 266 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( |
258 additions_map, additions_iterator_map, &next_smallest_prefix_additions); | 267 additions_map, additions_iterator_map, &next_smallest_prefix_additions); |
259 | 268 |
260 // Classical merge sort. | 269 // Classical merge sort. |
261 // The two constructs to merge are maps: old_prefixes_map, additions_map. | 270 // The two constructs to merge are maps: old_prefixes_map, additions_map. |
262 // At least one of the maps still has elements that need to be merged into the | 271 // At least one of the maps still has elements that need to be merged into the |
263 // new store. | 272 // new store. |
273 | |
274 // Keep track of the number of elements picked from the old map. This is used | |
275 // to determine which elements to drop based on the raw_removals. Note that | |
276 // picked is not the same as merged. A picked element isn't merged if its | |
277 // index is on the raw_removals list. | |
278 int total_picked_from_old = 0; | |
279 const int* removals_iter = raw_removals ? raw_removals->begin() : nullptr; | |
Nathan Parker
2016/07/18 19:54:35
If you're going to check raw_removals for null, th
vakh (use Gerrit instead)
2016/07/18 19:58:30
Done.
| |
264 while (old_has_unmerged || additions_has_unmerged) { | 280 while (old_has_unmerged || additions_has_unmerged) { |
265 // If the same hash prefix appears in the existing store and the additions | 281 // If the same hash prefix appears in the existing store and the additions |
266 // list, something is clearly wrong. Discard the update. | 282 // list, something is clearly wrong. Discard the update. |
267 if (old_has_unmerged && additions_has_unmerged && | 283 if (old_has_unmerged && additions_has_unmerged && |
268 next_smallest_prefix_old == next_smallest_prefix_additions) { | 284 next_smallest_prefix_old == next_smallest_prefix_additions) { |
269 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; | 285 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; |
270 } | 286 } |
271 | 287 |
272 // Select which map to pick the next hash prefix from to keep the result in | 288 // Select which map to pick the next hash prefix from to keep the result in |
273 // lexographically sorted order. | 289 // lexographically sorted order. |
274 bool pick_from_old = | 290 bool pick_from_old = |
275 old_has_unmerged && | 291 old_has_unmerged && |
276 (!additions_has_unmerged || | 292 (!additions_has_unmerged || |
277 (next_smallest_prefix_old < next_smallest_prefix_additions)); | 293 (next_smallest_prefix_old < next_smallest_prefix_additions)); |
278 | 294 |
279 PrefixSize next_smallest_prefix_size; | 295 PrefixSize next_smallest_prefix_size; |
280 if (pick_from_old) { | 296 if (pick_from_old) { |
281 next_smallest_prefix_size = next_smallest_prefix_old.size(); | 297 next_smallest_prefix_size = next_smallest_prefix_old.size(); |
282 | 298 |
283 // Append the smallest hash to the appropriate list. | |
284 hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old; | |
285 | |
286 // Update the iterator map, which means that we have merged one hash | 299 // Update the iterator map, which means that we have merged one hash |
287 // prefix of size |next_size_for_old| from the old store. | 300 // prefix of size |next_size_for_old| from the old store. |
288 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; | 301 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; |
289 | 302 |
303 if (!raw_removals || removals_iter == raw_removals->end() || | |
304 *removals_iter != total_picked_from_old) { | |
305 // Append the smallest hash to the appropriate list. | |
306 hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old; | |
307 } else { | |
308 // Element not added to new map. Move the removals iterator forward. | |
309 removals_iter++; | |
310 } | |
311 | |
312 total_picked_from_old++; | |
313 | |
290 // Find the next smallest unmerged element in the old store's map. | 314 // Find the next smallest unmerged element in the old store's map. |
291 old_has_unmerged = GetNextSmallestUnmergedPrefix( | 315 old_has_unmerged = GetNextSmallestUnmergedPrefix( |
292 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 316 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
293 } else { | 317 } else { |
294 next_smallest_prefix_size = next_smallest_prefix_additions.size(); | 318 next_smallest_prefix_size = next_smallest_prefix_additions.size(); |
295 | 319 |
296 // Append the smallest hash to the appropriate list. | 320 // Append the smallest hash to the appropriate list. |
297 hash_prefix_map_[next_smallest_prefix_size] += | 321 hash_prefix_map_[next_smallest_prefix_size] += |
298 next_smallest_prefix_additions; | 322 next_smallest_prefix_additions; |
299 | 323 |
300 // Update the iterator map, which means that we have merged one hash | 324 // Update the iterator map, which means that we have merged one hash |
301 // prefix of size |next_smallest_prefix_size| from the update. | 325 // prefix of size |next_smallest_prefix_size| from the update. |
302 additions_iterator_map[next_smallest_prefix_size] += | 326 additions_iterator_map[next_smallest_prefix_size] += |
303 next_smallest_prefix_size; | 327 next_smallest_prefix_size; |
304 | 328 |
305 // Find the next smallest unmerged element in the additions map. | 329 // Find the next smallest unmerged element in the additions map. |
306 additions_has_unmerged = | 330 additions_has_unmerged = |
307 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, | 331 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, |
308 &next_smallest_prefix_additions); | 332 &next_smallest_prefix_additions); |
309 } | 333 } |
310 } | 334 } |
311 | 335 |
312 return APPLY_UPDATE_SUCCESS; | 336 return (!raw_removals || removals_iter == raw_removals->end()) |
337 ? APPLY_UPDATE_SUCCESS | |
338 : REMOVALS_INDEX_TOO_LARGE; | |
313 } | 339 } |
314 | 340 |
315 StoreReadResult V4Store::ReadFromDisk() { | 341 StoreReadResult V4Store::ReadFromDisk() { |
316 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 342 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
317 | 343 |
318 std::string contents; | 344 std::string contents; |
319 bool read_success = base::ReadFileToString(store_path_, &contents); | 345 bool read_success = base::ReadFileToString(store_path_, &contents); |
320 if (!read_success) { | 346 if (!read_success) { |
321 return FILE_UNREADABLE_FAILURE; | 347 return FILE_UNREADABLE_FAILURE; |
322 } | 348 } |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
391 | 417 |
392 if (!base::Move(new_filename, store_path_)) { | 418 if (!base::Move(new_filename, store_path_)) { |
393 DVLOG(1) << "store_path_: " << store_path_.value(); | 419 DVLOG(1) << "store_path_: " << store_path_.value(); |
394 return UNABLE_TO_RENAME_FAILURE; | 420 return UNABLE_TO_RENAME_FAILURE; |
395 } | 421 } |
396 | 422 |
397 return WRITE_SUCCESS; | 423 return WRITE_SUCCESS; |
398 } | 424 } |
399 | 425 |
400 } // namespace safe_browsing | 426 } // namespace safe_browsing |
OLD | NEW |