| 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 29 matching lines...) Expand all Loading... |
| 96 } | 100 } |
| 97 | 101 |
| 98 void V4Store::ApplyUpdate( | 102 void V4Store::ApplyUpdate( |
| 99 std::unique_ptr<ListUpdateResponse> response, | 103 std::unique_ptr<ListUpdateResponse> response, |
| 100 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, | 104 const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner, |
| 101 UpdatedStoreReadyCallback callback) { | 105 UpdatedStoreReadyCallback callback) { |
| 102 std::unique_ptr<V4Store> new_store( | 106 std::unique_ptr<V4Store> new_store( |
| 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. Done: 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) { |
| 113 for (const auto& removal : response->removals()) { | 117 const RepeatedField<int32>* raw_removals = nullptr; |
| 118 size_t removals_size = response->removals_size(); |
| 119 DCHECK_LE(removals_size, 1u); |
| 120 if (removals_size == 1) { |
| 121 const ThreatEntrySet& removal = response->removals().Get(0); |
| 114 // TODO(vakh): Allow other compression types. | 122 // TODO(vakh): Allow other compression types. |
| 115 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 | 123 // See: https://bugs.chromium.org/p/chromium/issues/detail?id=624567 |
| 116 DCHECK_EQ(RAW, removal.compression_type()); | 124 DCHECK_EQ(RAW, removal.compression_type()); |
| 125 raw_removals = &removal.raw_indices().indices(); |
| 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 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 | 240 |
| 232 const HashPrefixes& existing_prefixes = | 241 const HashPrefixes& existing_prefixes = |
| 233 (*prefix_map_to_update)[prefix_size]; | 242 (*prefix_map_to_update)[prefix_size]; |
| 234 size_t existing_capacity = existing_prefixes.capacity(); | 243 size_t existing_capacity = existing_prefixes.capacity(); |
| 235 | 244 |
| 236 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + | 245 (*prefix_map_to_update)[prefix_size].reserve(existing_capacity + |
| 237 prefix_length_to_add); | 246 prefix_length_to_add); |
| 238 } | 247 } |
| 239 } | 248 } |
| 240 | 249 |
| 241 ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map, | 250 ApplyUpdateResult V4Store::MergeUpdate( |
| 242 const HashPrefixMap& additions_map) { | 251 const HashPrefixMap& old_prefixes_map, |
| 252 const HashPrefixMap& additions_map, |
| 253 const RepeatedField<int32>* raw_removals) { |
| 243 DCHECK(hash_prefix_map_.empty()); | 254 DCHECK(hash_prefix_map_.empty()); |
| 244 hash_prefix_map_.clear(); | 255 hash_prefix_map_.clear(); |
| 245 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); | 256 ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_); |
| 246 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); | 257 ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_); |
| 247 | 258 |
| 248 IteratorMap old_iterator_map; | 259 IteratorMap old_iterator_map; |
| 249 HashPrefix next_smallest_prefix_old; | 260 HashPrefix next_smallest_prefix_old; |
| 250 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); | 261 InitializeIteratorMap(old_prefixes_map, &old_iterator_map); |
| 251 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( | 262 bool old_has_unmerged = GetNextSmallestUnmergedPrefix( |
| 252 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 263 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
| 253 | 264 |
| 254 IteratorMap additions_iterator_map; | 265 IteratorMap additions_iterator_map; |
| 255 HashPrefix next_smallest_prefix_additions; | 266 HashPrefix next_smallest_prefix_additions; |
| 256 InitializeIteratorMap(additions_map, &additions_iterator_map); | 267 InitializeIteratorMap(additions_map, &additions_iterator_map); |
| 257 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( | 268 bool additions_has_unmerged = GetNextSmallestUnmergedPrefix( |
| 258 additions_map, additions_iterator_map, &next_smallest_prefix_additions); | 269 additions_map, additions_iterator_map, &next_smallest_prefix_additions); |
| 259 | 270 |
| 260 // Classical merge sort. | 271 // Classical merge sort. |
| 261 // The two constructs to merge are maps: old_prefixes_map, additions_map. | 272 // 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 | 273 // At least one of the maps still has elements that need to be merged into the |
| 263 // new store. | 274 // new store. |
| 275 |
| 276 // Keep track of the number of elements picked from the old map. This is used |
| 277 // to determine which elements to drop based on the raw_removals. Note that |
| 278 // picked is not the same as merged. A picked element isn't merged if its |
| 279 // index is on the raw_removals list. |
| 280 int total_picked_from_old = 0; |
| 281 const int* removals_iter = raw_removals ? raw_removals->begin() : nullptr; |
| 264 while (old_has_unmerged || additions_has_unmerged) { | 282 while (old_has_unmerged || additions_has_unmerged) { |
| 265 // If the same hash prefix appears in the existing store and the additions | 283 // If the same hash prefix appears in the existing store and the additions |
| 266 // list, something is clearly wrong. Discard the update. | 284 // list, something is clearly wrong. Discard the update. |
| 267 if (old_has_unmerged && additions_has_unmerged && | 285 if (old_has_unmerged && additions_has_unmerged && |
| 268 next_smallest_prefix_old == next_smallest_prefix_additions) { | 286 next_smallest_prefix_old == next_smallest_prefix_additions) { |
| 269 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; | 287 return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE; |
| 270 } | 288 } |
| 271 | 289 |
| 272 // Select which map to pick the next hash prefix from to keep the result in | 290 // Select which map to pick the next hash prefix from to keep the result in |
| 273 // lexographically sorted order. | 291 // lexographically sorted order. |
| 274 bool pick_from_old = | 292 bool pick_from_old = |
| 275 old_has_unmerged && | 293 old_has_unmerged && |
| 276 (!additions_has_unmerged || | 294 (!additions_has_unmerged || |
| 277 (next_smallest_prefix_old < next_smallest_prefix_additions)); | 295 (next_smallest_prefix_old < next_smallest_prefix_additions)); |
| 278 | 296 |
| 279 PrefixSize next_smallest_prefix_size; | 297 PrefixSize next_smallest_prefix_size; |
| 280 if (pick_from_old) { | 298 if (pick_from_old) { |
| 281 next_smallest_prefix_size = next_smallest_prefix_old.size(); | 299 next_smallest_prefix_size = next_smallest_prefix_old.size(); |
| 282 | 300 |
| 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 | 301 // Update the iterator map, which means that we have merged one hash |
| 287 // prefix of size |next_size_for_old| from the old store. | 302 // prefix of size |next_size_for_old| from the old store. |
| 288 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; | 303 old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size; |
| 289 | 304 |
| 305 if (!raw_removals || removals_iter == raw_removals->end() || |
| 306 *removals_iter != total_picked_from_old) { |
| 307 // Append the smallest hash to the appropriate list. |
| 308 hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old; |
| 309 } else { |
| 310 // Element not added to new map. Move the removals iterator forward. |
| 311 removals_iter++; |
| 312 } |
| 313 |
| 314 total_picked_from_old++; |
| 315 |
| 290 // Find the next smallest unmerged element in the old store's map. | 316 // Find the next smallest unmerged element in the old store's map. |
| 291 old_has_unmerged = GetNextSmallestUnmergedPrefix( | 317 old_has_unmerged = GetNextSmallestUnmergedPrefix( |
| 292 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); | 318 old_prefixes_map, old_iterator_map, &next_smallest_prefix_old); |
| 293 } else { | 319 } else { |
| 294 next_smallest_prefix_size = next_smallest_prefix_additions.size(); | 320 next_smallest_prefix_size = next_smallest_prefix_additions.size(); |
| 295 | 321 |
| 296 // Append the smallest hash to the appropriate list. | 322 // Append the smallest hash to the appropriate list. |
| 297 hash_prefix_map_[next_smallest_prefix_size] += | 323 hash_prefix_map_[next_smallest_prefix_size] += |
| 298 next_smallest_prefix_additions; | 324 next_smallest_prefix_additions; |
| 299 | 325 |
| 300 // Update the iterator map, which means that we have merged one hash | 326 // Update the iterator map, which means that we have merged one hash |
| 301 // prefix of size |next_smallest_prefix_size| from the update. | 327 // prefix of size |next_smallest_prefix_size| from the update. |
| 302 additions_iterator_map[next_smallest_prefix_size] += | 328 additions_iterator_map[next_smallest_prefix_size] += |
| 303 next_smallest_prefix_size; | 329 next_smallest_prefix_size; |
| 304 | 330 |
| 305 // Find the next smallest unmerged element in the additions map. | 331 // Find the next smallest unmerged element in the additions map. |
| 306 additions_has_unmerged = | 332 additions_has_unmerged = |
| 307 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, | 333 GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map, |
| 308 &next_smallest_prefix_additions); | 334 &next_smallest_prefix_additions); |
| 309 } | 335 } |
| 310 } | 336 } |
| 311 | 337 |
| 312 return APPLY_UPDATE_SUCCESS; | 338 return (!raw_removals || removals_iter == raw_removals->end()) |
| 339 ? APPLY_UPDATE_SUCCESS |
| 340 : REMOVALS_INDEX_TOO_LARGE; |
| 313 } | 341 } |
| 314 | 342 |
| 315 StoreReadResult V4Store::ReadFromDisk() { | 343 StoreReadResult V4Store::ReadFromDisk() { |
| 316 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 344 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 317 | 345 |
| 318 std::string contents; | 346 std::string contents; |
| 319 bool read_success = base::ReadFileToString(store_path_, &contents); | 347 bool read_success = base::ReadFileToString(store_path_, &contents); |
| 320 if (!read_success) { | 348 if (!read_success) { |
| 321 return FILE_UNREADABLE_FAILURE; | 349 return FILE_UNREADABLE_FAILURE; |
| 322 } | 350 } |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 | 419 |
| 392 if (!base::Move(new_filename, store_path_)) { | 420 if (!base::Move(new_filename, store_path_)) { |
| 393 DVLOG(1) << "store_path_: " << store_path_.value(); | 421 DVLOG(1) << "store_path_: " << store_path_.value(); |
| 394 return UNABLE_TO_RENAME_FAILURE; | 422 return UNABLE_TO_RENAME_FAILURE; |
| 395 } | 423 } |
| 396 | 424 |
| 397 return WRITE_SUCCESS; | 425 return WRITE_SUCCESS; |
| 398 } | 426 } |
| 399 | 427 |
| 400 } // namespace safe_browsing | 428 } // namespace safe_browsing |
| OLD | NEW |