Chromium Code Reviews| 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 = nullptr; | |
| 113 for (const auto& removal : response->removals()) { | 119 for (const auto& removal : response->removals()) { |
|
Nathan Parker
2016/07/18 20:42:39
This still iterates over the loop, though you expe
vakh (use Gerrit instead)
2016/07/18 20:54:13
Done.
| |
| 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; | |
| 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 |