| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/browser/safe_browsing/safe_browsing_store_file.h" | 5 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" |
| 6 | 6 |
| 7 #include "base/md5.h" | 7 #include "base/md5.h" |
| 8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 | 9 |
| 10 namespace { | 10 namespace { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 // Although fseek takes negative values, for this case, we only want | 41 // Although fseek takes negative values, for this case, we only want |
| 42 // to skip forward. | 42 // to skip forward. |
| 43 DCHECK(static_cast<long>(bytes) >= 0); | 43 DCHECK(static_cast<long>(bytes) >= 0); |
| 44 if (static_cast<long>(bytes) < 0) | 44 if (static_cast<long>(bytes) < 0) |
| 45 return false; | 45 return false; |
| 46 int rv = fseek(fp, static_cast<long>(bytes), SEEK_CUR); | 46 int rv = fseek(fp, static_cast<long>(bytes), SEEK_CUR); |
| 47 DCHECK_EQ(rv, 0); | 47 DCHECK_EQ(rv, 0); |
| 48 return rv == 0; | 48 return rv == 0; |
| 49 } | 49 } |
| 50 | 50 |
| 51 // Read an array of |nmemb| items from |fp| into |ptr|, and fold the | 51 // Read from |fp| into |item|, and fold the input data into the |
| 52 // input data into the checksum in |context|, if non-NULL. Return | 52 // checksum in |context|, if non-NULL. Return true on success. |
| 53 // true on success. | |
| 54 template <class T> | 53 template <class T> |
| 55 bool ReadArray(T* ptr, size_t nmemb, FILE* fp, base::MD5Context* context) { | 54 bool ReadItem(T* item, FILE* fp, base::MD5Context* context) { |
| 56 const size_t ret = fread(ptr, sizeof(T), nmemb, fp); | 55 const size_t ret = fread(item, sizeof(T), 1, fp); |
| 57 if (ret != nmemb) | 56 if (ret != 1) |
| 58 return false; | 57 return false; |
| 59 | 58 |
| 60 if (context) { | 59 if (context) { |
| 61 base::MD5Update(context, | 60 base::MD5Update(context, |
| 62 base::StringPiece(reinterpret_cast<char*>(ptr), | 61 base::StringPiece(reinterpret_cast<char*>(item), |
| 63 sizeof(T) * nmemb)); | 62 sizeof(T))); |
| 64 } | 63 } |
| 65 return true; | 64 return true; |
| 66 } | 65 } |
| 67 | 66 |
| 68 // Write an array of |nmemb| items from |ptr| to |fp|, and fold the | 67 // Write |item| to |fp|, and fold the output data into the checksum in |
| 69 // output data into the checksum in |context|, if non-NULL. Return | 68 // |context|, if non-NULL. Return true on success. |
| 70 // true on success. | |
| 71 template <class T> | 69 template <class T> |
| 72 bool WriteArray(const T* ptr, size_t nmemb, FILE* fp, | 70 bool WriteItem(const T& item, FILE* fp, base::MD5Context* context) { |
| 73 base::MD5Context* context) { | 71 const size_t ret = fwrite(&item, sizeof(T), 1, fp); |
| 74 const size_t ret = fwrite(ptr, sizeof(T), nmemb, fp); | 72 if (ret != 1) |
| 75 if (ret != nmemb) | |
| 76 return false; | 73 return false; |
| 77 | 74 |
| 78 if (context) { | 75 if (context) { |
| 79 base::MD5Update(context, | 76 base::MD5Update(context, |
| 80 base::StringPiece(reinterpret_cast<const char*>(ptr), | 77 base::StringPiece(reinterpret_cast<const char*>(&item), |
| 81 sizeof(T) * nmemb)); | 78 sizeof(T))); |
| 82 } | 79 } |
| 83 | 80 |
| 84 return true; | 81 return true; |
| 85 } | 82 } |
| 86 | 83 |
| 87 // Expand |values| to fit |count| new items, read those items from | 84 // Read |count| items into |values| from |fp|, and fold them into the |
| 88 // |fp| and fold them into the checksum in |context|. Returns true on | 85 // checksum in |context|. Returns true on success. |
| 89 // success. | 86 template <typename CT> |
| 90 template <class T> | 87 bool ReadToContainer(CT* values, size_t count, FILE* fp, |
| 91 bool ReadToVector(std::vector<T>* values, size_t count, FILE* fp, | 88 base::MD5Context* context) { |
| 92 base::MD5Context* context) { | |
| 93 // Pointers into an empty vector may not be valid. | |
| 94 if (!count) | 89 if (!count) |
| 95 return true; | 90 return true; |
| 96 | 91 |
| 97 // Grab the size for purposes of finding where to read to. The | 92 for (size_t i = 0; i < count; ++i) { |
| 98 // resize could invalidate any iterator captured here. | 93 typename CT::value_type value; |
| 99 const size_t original_size = values->size(); | 94 if (!ReadItem(&value, fp, context)) |
| 100 values->resize(original_size + count); | 95 return false; |
| 101 | 96 |
| 102 // Sayeth Herb Sutter: Vectors are guaranteed to be contiguous. So | 97 // push_back() is more obvious, but coded this way std::set can |
| 103 // get a pointer to where to read the data to. | 98 // also be read. |
| 104 T* ptr = &((*values)[original_size]); | 99 values->insert(values->end(), value); |
| 105 if (!ReadArray(ptr, count, fp, context)) { | |
| 106 values->resize(original_size); | |
| 107 return false; | |
| 108 } | 100 } |
| 109 | 101 |
| 110 return true; | 102 return true; |
| 111 } | 103 } |
| 112 | 104 |
| 113 // Write all of |values| to |fp|, and fold the data into the checksum | 105 // Write all of |values| to |fp|, and fold the data into the checksum |
| 114 // in |context|, if non-NULL. Returns true on succsess. | 106 // in |context|, if non-NULL. Returns true on succsess. |
| 115 template <class T> | 107 template <typename CT> |
| 116 bool WriteVector(const std::vector<T>& values, FILE* fp, | 108 bool WriteContainer(const CT& values, FILE* fp, |
| 117 base::MD5Context* context) { | 109 base::MD5Context* context) { |
| 118 // Pointers into empty vectors may not be valid. | |
| 119 if (values.empty()) | 110 if (values.empty()) |
| 120 return true; | 111 return true; |
| 121 | 112 |
| 122 // Sayeth Herb Sutter: Vectors are guaranteed to be contiguous. So | 113 for (typename CT::const_iterator iter = values.begin(); |
| 123 // get a pointer to where to write from. | 114 iter != values.end(); ++iter) { |
| 124 const T* ptr = &(values[0]); | 115 if (!WriteItem(*iter, fp, context)) |
| 125 return WriteArray(ptr, values.size(), fp, context); | 116 return false; |
| 126 } | 117 } |
| 127 | |
| 128 // Read an array of |count| integers and add them to |values|. | |
| 129 // Returns true on success. | |
| 130 bool ReadToChunkSet(std::set<int32>* values, size_t count, FILE* fp, | |
| 131 base::MD5Context* context) { | |
| 132 if (!count) | |
| 133 return true; | |
| 134 | |
| 135 std::vector<int32> flat_values; | |
| 136 if (!ReadToVector(&flat_values, count, fp, context)) | |
| 137 return false; | |
| 138 | |
| 139 values->insert(flat_values.begin(), flat_values.end()); | |
| 140 return true; | 118 return true; |
| 141 } | 119 } |
| 142 | 120 |
| 143 // Write the contents of |values| as an array of integers. Returns | |
| 144 // true on success. | |
| 145 bool WriteChunkSet(const std::set<int32>& values, FILE* fp, | |
| 146 base::MD5Context* context) { | |
| 147 if (values.empty()) | |
| 148 return true; | |
| 149 | |
| 150 const std::vector<int32> flat_values(values.begin(), values.end()); | |
| 151 return WriteVector(flat_values, fp, context); | |
| 152 } | |
| 153 | |
| 154 // Delete the chunks in |deleted| from |chunks|. | 121 // Delete the chunks in |deleted| from |chunks|. |
| 155 void DeleteChunksFromSet(const base::hash_set<int32>& deleted, | 122 void DeleteChunksFromSet(const base::hash_set<int32>& deleted, |
| 156 std::set<int32>* chunks) { | 123 std::set<int32>* chunks) { |
| 157 for (std::set<int32>::iterator iter = chunks->begin(); | 124 for (std::set<int32>::iterator iter = chunks->begin(); |
| 158 iter != chunks->end();) { | 125 iter != chunks->end();) { |
| 159 std::set<int32>::iterator prev = iter++; | 126 std::set<int32>::iterator prev = iter++; |
| 160 if (deleted.count(*prev) > 0) | 127 if (deleted.count(*prev) > 0) |
| 161 chunks->erase(prev); | 128 chunks->erase(prev); |
| 162 } | 129 } |
| 163 } | 130 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 184 | 151 |
| 185 return true; | 152 return true; |
| 186 } | 153 } |
| 187 | 154 |
| 188 // This a helper function that reads header to |header|. Returns true if the | 155 // This a helper function that reads header to |header|. Returns true if the |
| 189 // magic number is correct and santiy check passes. | 156 // magic number is correct and santiy check passes. |
| 190 bool ReadAndVerifyHeader(const FilePath& filename, | 157 bool ReadAndVerifyHeader(const FilePath& filename, |
| 191 FILE* fp, | 158 FILE* fp, |
| 192 FileHeader* header, | 159 FileHeader* header, |
| 193 base::MD5Context* context) { | 160 base::MD5Context* context) { |
| 194 if (!ReadArray(header, 1, fp, context)) | 161 if (!ReadItem(header, fp, context)) |
| 195 return false; | 162 return false; |
| 196 if (header->magic != kFileMagic || header->version != kFileVersion) | 163 if (header->magic != kFileMagic || header->version != kFileVersion) |
| 197 return false; | 164 return false; |
| 198 if (!FileHeaderSanityCheck(filename, *header)) | 165 if (!FileHeaderSanityCheck(filename, *header)) |
| 199 return false; | 166 return false; |
| 200 return true; | 167 return true; |
| 201 } | 168 } |
| 202 | 169 |
| 203 } // namespace | 170 } // namespace |
| 204 | 171 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 | 253 |
| 287 bool SafeBrowsingStoreFile::BeginChunk() { | 254 bool SafeBrowsingStoreFile::BeginChunk() { |
| 288 return ClearChunkBuffers(); | 255 return ClearChunkBuffers(); |
| 289 } | 256 } |
| 290 | 257 |
| 291 bool SafeBrowsingStoreFile::WriteAddPrefix(int32 chunk_id, SBPrefix prefix) { | 258 bool SafeBrowsingStoreFile::WriteAddPrefix(int32 chunk_id, SBPrefix prefix) { |
| 292 add_prefixes_.push_back(SBAddPrefix(chunk_id, prefix)); | 259 add_prefixes_.push_back(SBAddPrefix(chunk_id, prefix)); |
| 293 return true; | 260 return true; |
| 294 } | 261 } |
| 295 | 262 |
| 296 bool SafeBrowsingStoreFile::GetAddPrefixes( | 263 bool SafeBrowsingStoreFile::GetAddPrefixes(SBAddPrefixes* add_prefixes) { |
| 297 std::vector<SBAddPrefix>* add_prefixes) { | |
| 298 add_prefixes->clear(); | 264 add_prefixes->clear(); |
| 299 | 265 |
| 300 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); | 266 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); |
| 301 if (file.get() == NULL) return false; | 267 if (file.get() == NULL) return false; |
| 302 | 268 |
| 303 FileHeader header; | 269 FileHeader header; |
| 304 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) | 270 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) |
| 305 return OnCorruptDatabase(); | 271 return OnCorruptDatabase(); |
| 306 | 272 |
| 307 size_t add_prefix_offset = header.add_chunk_count * sizeof(int32) + | 273 size_t add_prefix_offset = header.add_chunk_count * sizeof(int32) + |
| 308 header.sub_chunk_count * sizeof(int32); | 274 header.sub_chunk_count * sizeof(int32); |
| 309 if (!FileSkip(add_prefix_offset, file.get())) | 275 if (!FileSkip(add_prefix_offset, file.get())) |
| 310 return false; | 276 return false; |
| 311 | 277 |
| 312 if (!ReadToVector(add_prefixes, header.add_prefix_count, file.get(), NULL)) | 278 if (!ReadToContainer(add_prefixes, header.add_prefix_count, file.get(), NULL)) |
| 313 return false; | 279 return false; |
| 314 | 280 |
| 315 return true; | 281 return true; |
| 316 } | 282 } |
| 317 | 283 |
| 318 bool SafeBrowsingStoreFile::GetAddFullHashes( | 284 bool SafeBrowsingStoreFile::GetAddFullHashes( |
| 319 std::vector<SBAddFullHash>* add_full_hashes) { | 285 std::vector<SBAddFullHash>* add_full_hashes) { |
| 320 add_full_hashes->clear(); | 286 add_full_hashes->clear(); |
| 321 | 287 |
| 322 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); | 288 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); |
| 323 if (file.get() == NULL) return false; | 289 if (file.get() == NULL) return false; |
| 324 | 290 |
| 325 FileHeader header; | 291 FileHeader header; |
| 326 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) | 292 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) |
| 327 return OnCorruptDatabase(); | 293 return OnCorruptDatabase(); |
| 328 | 294 |
| 329 size_t offset = | 295 size_t offset = |
| 330 header.add_chunk_count * sizeof(int32) + | 296 header.add_chunk_count * sizeof(int32) + |
| 331 header.sub_chunk_count * sizeof(int32) + | 297 header.sub_chunk_count * sizeof(int32) + |
| 332 header.add_prefix_count * sizeof(SBAddPrefix) + | 298 header.add_prefix_count * sizeof(SBAddPrefix) + |
| 333 header.sub_prefix_count * sizeof(SBSubPrefix); | 299 header.sub_prefix_count * sizeof(SBSubPrefix); |
| 334 if (!FileSkip(offset, file.get())) | 300 if (!FileSkip(offset, file.get())) |
| 335 return false; | 301 return false; |
| 336 | 302 |
| 337 return ReadToVector(add_full_hashes, | 303 return ReadToContainer(add_full_hashes, |
| 338 header.add_hash_count, | 304 header.add_hash_count, |
| 339 file.get(), | 305 file.get(), |
| 340 NULL); | 306 NULL); |
| 341 } | 307 } |
| 342 | 308 |
| 343 bool SafeBrowsingStoreFile::WriteAddHash(int32 chunk_id, | 309 bool SafeBrowsingStoreFile::WriteAddHash(int32 chunk_id, |
| 344 base::Time receive_time, | 310 base::Time receive_time, |
| 345 const SBFullHash& full_hash) { | 311 const SBFullHash& full_hash) { |
| 346 add_hashes_.push_back(SBAddFullHash(chunk_id, receive_time, full_hash)); | 312 add_hashes_.push_back(SBAddFullHash(chunk_id, receive_time, full_hash)); |
| 347 return true; | 313 return true; |
| 348 } | 314 } |
| 349 | 315 |
| 350 bool SafeBrowsingStoreFile::WriteSubPrefix(int32 chunk_id, | 316 bool SafeBrowsingStoreFile::WriteSubPrefix(int32 chunk_id, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 // Structures should all be clear unless something bad happened. | 352 // Structures should all be clear unless something bad happened. |
| 387 DCHECK(add_chunks_cache_.empty()); | 353 DCHECK(add_chunks_cache_.empty()); |
| 388 DCHECK(sub_chunks_cache_.empty()); | 354 DCHECK(sub_chunks_cache_.empty()); |
| 389 DCHECK(add_del_cache_.empty()); | 355 DCHECK(add_del_cache_.empty()); |
| 390 DCHECK(sub_del_cache_.empty()); | 356 DCHECK(sub_del_cache_.empty()); |
| 391 DCHECK(add_prefixes_.empty()); | 357 DCHECK(add_prefixes_.empty()); |
| 392 DCHECK(sub_prefixes_.empty()); | 358 DCHECK(sub_prefixes_.empty()); |
| 393 DCHECK(add_hashes_.empty()); | 359 DCHECK(add_hashes_.empty()); |
| 394 DCHECK(sub_hashes_.empty()); | 360 DCHECK(sub_hashes_.empty()); |
| 395 DCHECK_EQ(chunks_written_, 0); | 361 DCHECK_EQ(chunks_written_, 0); |
| 396 add_prefixes_added_ = 0; | |
| 397 | 362 |
| 398 // Since the following code will already hit the profile looking for | 363 // Since the following code will already hit the profile looking for |
| 399 // database files, this is a reasonable to time delete any old | 364 // database files, this is a reasonable to time delete any old |
| 400 // files. | 365 // files. |
| 401 CheckForOriginalAndDelete(filename_); | 366 CheckForOriginalAndDelete(filename_); |
| 402 | 367 |
| 403 corruption_seen_ = false; | 368 corruption_seen_ = false; |
| 404 | 369 |
| 405 const FilePath new_filename = TemporaryFileForFilename(filename_); | 370 const FilePath new_filename = TemporaryFileForFilename(filename_); |
| 406 file_util::ScopedFILE new_file(file_util::OpenFile(new_filename, "wb+")); | 371 file_util::ScopedFILE new_file(file_util::OpenFile(new_filename, "wb+")); |
| 407 if (new_file.get() == NULL) | 372 if (new_file.get() == NULL) |
| 408 return false; | 373 return false; |
| 409 | 374 |
| 410 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); | 375 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); |
| 411 empty_ = (file.get() == NULL); | 376 empty_ = (file.get() == NULL); |
| 412 if (empty_) { | 377 if (empty_) { |
| 413 // If the file exists but cannot be opened, try to delete it (not | 378 // If the file exists but cannot be opened, try to delete it (not |
| 414 // deleting directly, the bloom filter needs to be deleted, too). | 379 // deleting directly, the bloom filter needs to be deleted, too). |
| 415 if (file_util::PathExists(filename_)) | 380 if (file_util::PathExists(filename_)) |
| 416 return OnCorruptDatabase(); | 381 return OnCorruptDatabase(); |
| 417 | 382 |
| 418 new_file_.swap(new_file); | 383 new_file_.swap(new_file); |
| 419 return true; | 384 return true; |
| 420 } | 385 } |
| 421 | 386 |
| 422 FileHeader header; | 387 FileHeader header; |
| 423 if (!ReadArray(&header, 1, file.get(), NULL)) | 388 if (!ReadItem(&header, file.get(), NULL)) |
| 424 return OnCorruptDatabase(); | 389 return OnCorruptDatabase(); |
| 425 | 390 |
| 426 if (header.magic != kFileMagic || header.version != kFileVersion) { | 391 if (header.magic != kFileMagic || header.version != kFileVersion) { |
| 427 if (!strcmp(reinterpret_cast<char*>(&header.magic), "SQLite format 3")) { | 392 if (!strcmp(reinterpret_cast<char*>(&header.magic), "SQLite format 3")) { |
| 428 RecordFormatEvent(FORMAT_EVENT_FOUND_SQLITE); | 393 RecordFormatEvent(FORMAT_EVENT_FOUND_SQLITE); |
| 429 } else { | 394 } else { |
| 430 RecordFormatEvent(FORMAT_EVENT_FOUND_UNKNOWN); | 395 RecordFormatEvent(FORMAT_EVENT_FOUND_UNKNOWN); |
| 431 } | 396 } |
| 432 | 397 |
| 433 // Close the file so that it can be deleted. | 398 // Close the file so that it can be deleted. |
| 434 file.reset(); | 399 file.reset(); |
| 435 | 400 |
| 436 return OnCorruptDatabase(); | 401 return OnCorruptDatabase(); |
| 437 } | 402 } |
| 438 | 403 |
| 439 // TODO(shess): Under POSIX it is possible that this could size a | 404 // TODO(shess): Under POSIX it is possible that this could size a |
| 440 // file different from the file which was opened. | 405 // file different from the file which was opened. |
| 441 if (!FileHeaderSanityCheck(filename_, header)) | 406 if (!FileHeaderSanityCheck(filename_, header)) |
| 442 return OnCorruptDatabase(); | 407 return OnCorruptDatabase(); |
| 443 | 408 |
| 444 // Pull in the chunks-seen data for purposes of implementing | 409 // Pull in the chunks-seen data for purposes of implementing |
| 445 // |GetAddChunks()| and |GetSubChunks()|. This data is sent up to | 410 // |GetAddChunks()| and |GetSubChunks()|. This data is sent up to |
| 446 // the server at the beginning of an update. | 411 // the server at the beginning of an update. |
| 447 if (!ReadToChunkSet(&add_chunks_cache_, header.add_chunk_count, | 412 if (!ReadToContainer(&add_chunks_cache_, header.add_chunk_count, |
| 448 file.get(), NULL) || | 413 file.get(), NULL) || |
| 449 !ReadToChunkSet(&sub_chunks_cache_, header.sub_chunk_count, | 414 !ReadToContainer(&sub_chunks_cache_, header.sub_chunk_count, |
| 450 file.get(), NULL)) | 415 file.get(), NULL)) |
| 451 return OnCorruptDatabase(); | 416 return OnCorruptDatabase(); |
| 452 | 417 |
| 453 file_.swap(file); | 418 file_.swap(file); |
| 454 new_file_.swap(new_file); | 419 new_file_.swap(new_file); |
| 455 return true; | 420 return true; |
| 456 } | 421 } |
| 457 | 422 |
| 458 bool SafeBrowsingStoreFile::FinishChunk() { | 423 bool SafeBrowsingStoreFile::FinishChunk() { |
| 459 if (!add_prefixes_.size() && !sub_prefixes_.size() && | 424 if (!add_prefixes_.size() && !sub_prefixes_.size() && |
| 460 !add_hashes_.size() && !sub_hashes_.size()) | 425 !add_hashes_.size() && !sub_hashes_.size()) |
| 461 return true; | 426 return true; |
| 462 | 427 |
| 463 ChunkHeader header; | 428 ChunkHeader header; |
| 464 header.add_prefix_count = add_prefixes_.size(); | 429 header.add_prefix_count = add_prefixes_.size(); |
| 465 header.sub_prefix_count = sub_prefixes_.size(); | 430 header.sub_prefix_count = sub_prefixes_.size(); |
| 466 header.add_hash_count = add_hashes_.size(); | 431 header.add_hash_count = add_hashes_.size(); |
| 467 header.sub_hash_count = sub_hashes_.size(); | 432 header.sub_hash_count = sub_hashes_.size(); |
| 468 if (!WriteArray(&header, 1, new_file_.get(), NULL)) | 433 if (!WriteItem(header, new_file_.get(), NULL)) |
| 469 return false; | 434 return false; |
| 470 | 435 |
| 471 if (!WriteVector(add_prefixes_, new_file_.get(), NULL) || | 436 if (!WriteContainer(add_prefixes_, new_file_.get(), NULL) || |
| 472 !WriteVector(sub_prefixes_, new_file_.get(), NULL) || | 437 !WriteContainer(sub_prefixes_, new_file_.get(), NULL) || |
| 473 !WriteVector(add_hashes_, new_file_.get(), NULL) || | 438 !WriteContainer(add_hashes_, new_file_.get(), NULL) || |
| 474 !WriteVector(sub_hashes_, new_file_.get(), NULL)) | 439 !WriteContainer(sub_hashes_, new_file_.get(), NULL)) |
| 475 return false; | 440 return false; |
| 476 | 441 |
| 477 ++chunks_written_; | 442 ++chunks_written_; |
| 478 add_prefixes_added_ += add_prefixes_.size(); | |
| 479 | 443 |
| 480 // Clear everything to save memory. | 444 // Clear everything to save memory. |
| 481 return ClearChunkBuffers(); | 445 return ClearChunkBuffers(); |
| 482 } | 446 } |
| 483 | 447 |
| 484 bool SafeBrowsingStoreFile::DoUpdate( | 448 bool SafeBrowsingStoreFile::DoUpdate( |
| 485 const std::vector<SBAddFullHash>& pending_adds, | 449 const std::vector<SBAddFullHash>& pending_adds, |
| 486 const std::set<SBPrefix>& prefix_misses, | 450 const std::set<SBPrefix>& prefix_misses, |
| 487 std::vector<SBAddPrefix>* add_prefixes_result, | 451 SBAddPrefixes* add_prefixes_result, |
| 488 std::vector<SBAddFullHash>* add_full_hashes_result) { | 452 std::vector<SBAddFullHash>* add_full_hashes_result) { |
| 489 DCHECK(file_.get() || empty_); | 453 DCHECK(file_.get() || empty_); |
| 490 DCHECK(new_file_.get()); | 454 DCHECK(new_file_.get()); |
| 491 CHECK(add_prefixes_result); | 455 CHECK(add_prefixes_result); |
| 492 CHECK(add_full_hashes_result); | 456 CHECK(add_full_hashes_result); |
| 493 | 457 |
| 494 std::vector<SBAddPrefix> add_prefixes; | 458 SBAddPrefixes add_prefixes; |
| 495 std::vector<SBSubPrefix> sub_prefixes; | 459 std::vector<SBSubPrefix> sub_prefixes; |
| 496 std::vector<SBAddFullHash> add_full_hashes; | 460 std::vector<SBAddFullHash> add_full_hashes; |
| 497 std::vector<SBSubFullHash> sub_full_hashes; | 461 std::vector<SBSubFullHash> sub_full_hashes; |
| 498 | 462 |
| 499 // Read original data into the vectors. | 463 // Read original data into the vectors. |
| 500 if (!empty_) { | 464 if (!empty_) { |
| 501 DCHECK(file_.get()); | 465 DCHECK(file_.get()); |
| 502 | 466 |
| 503 if (!FileRewind(file_.get())) | 467 if (!FileRewind(file_.get())) |
| 504 return OnCorruptDatabase(); | 468 return OnCorruptDatabase(); |
| 505 | 469 |
| 506 base::MD5Context context; | 470 base::MD5Context context; |
| 507 base::MD5Init(&context); | 471 base::MD5Init(&context); |
| 508 | 472 |
| 509 // Read the file header and make sure it looks right. | 473 // Read the file header and make sure it looks right. |
| 510 FileHeader header; | 474 FileHeader header; |
| 511 if (!ReadAndVerifyHeader(filename_, file_.get(), &header, &context)) | 475 if (!ReadAndVerifyHeader(filename_, file_.get(), &header, &context)) |
| 512 return OnCorruptDatabase(); | 476 return OnCorruptDatabase(); |
| 513 | 477 |
| 514 // Re-read the chunks-seen data to get to the later data in the | 478 // Re-read the chunks-seen data to get to the later data in the |
| 515 // file and calculate the checksum. No new elements should be | 479 // file and calculate the checksum. No new elements should be |
| 516 // added to the sets. | 480 // added to the sets. |
| 517 if (!ReadToChunkSet(&add_chunks_cache_, header.add_chunk_count, | 481 if (!ReadToContainer(&add_chunks_cache_, header.add_chunk_count, |
| 518 file_.get(), &context) || | 482 file_.get(), &context) || |
| 519 !ReadToChunkSet(&sub_chunks_cache_, header.sub_chunk_count, | 483 !ReadToContainer(&sub_chunks_cache_, header.sub_chunk_count, |
| 520 file_.get(), &context)) | 484 file_.get(), &context)) |
| 521 return OnCorruptDatabase(); | 485 return OnCorruptDatabase(); |
| 522 | 486 |
| 523 add_prefixes.reserve(header.add_prefix_count + add_prefixes_added_); | 487 if (!ReadToContainer(&add_prefixes, header.add_prefix_count, |
| 524 | 488 file_.get(), &context) || |
| 525 if (!ReadToVector(&add_prefixes, header.add_prefix_count, | 489 !ReadToContainer(&sub_prefixes, header.sub_prefix_count, |
| 526 file_.get(), &context) || | 490 file_.get(), &context) || |
| 527 !ReadToVector(&sub_prefixes, header.sub_prefix_count, | 491 !ReadToContainer(&add_full_hashes, header.add_hash_count, |
| 528 file_.get(), &context) || | 492 file_.get(), &context) || |
| 529 !ReadToVector(&add_full_hashes, header.add_hash_count, | 493 !ReadToContainer(&sub_full_hashes, header.sub_hash_count, |
| 530 file_.get(), &context) || | 494 file_.get(), &context)) |
| 531 !ReadToVector(&sub_full_hashes, header.sub_hash_count, | |
| 532 file_.get(), &context)) | |
| 533 return OnCorruptDatabase(); | 495 return OnCorruptDatabase(); |
| 534 | 496 |
| 535 // Calculate the digest to this point. | 497 // Calculate the digest to this point. |
| 536 base::MD5Digest calculated_digest; | 498 base::MD5Digest calculated_digest; |
| 537 base::MD5Final(&calculated_digest, &context); | 499 base::MD5Final(&calculated_digest, &context); |
| 538 | 500 |
| 539 // Read the stored checksum and verify it. | 501 // Read the stored checksum and verify it. |
| 540 base::MD5Digest file_digest; | 502 base::MD5Digest file_digest; |
| 541 if (!ReadArray(&file_digest, 1, file_.get(), NULL)) | 503 if (!ReadItem(&file_digest, file_.get(), NULL)) |
| 542 return OnCorruptDatabase(); | 504 return OnCorruptDatabase(); |
| 543 | 505 |
| 544 if (0 != memcmp(&file_digest, &calculated_digest, sizeof(file_digest))) | 506 if (0 != memcmp(&file_digest, &calculated_digest, sizeof(file_digest))) |
| 545 return OnCorruptDatabase(); | 507 return OnCorruptDatabase(); |
| 546 | 508 |
| 547 // Close the file so we can later rename over it. | 509 // Close the file so we can later rename over it. |
| 548 file_.reset(); | 510 file_.reset(); |
| 549 } else { | |
| 550 add_prefixes.reserve(add_prefixes_added_); | |
| 551 } | 511 } |
| 552 DCHECK(!file_.get()); | 512 DCHECK(!file_.get()); |
| 553 | 513 |
| 554 // Rewind the temporary storage. | 514 // Rewind the temporary storage. |
| 555 if (!FileRewind(new_file_.get())) | 515 if (!FileRewind(new_file_.get())) |
| 556 return false; | 516 return false; |
| 557 | 517 |
| 558 // Get chunk file's size for validating counts. | 518 // Get chunk file's size for validating counts. |
| 559 int64 size = 0; | 519 int64 size = 0; |
| 560 if (!file_util::GetFileSize(TemporaryFileForFilename(filename_), &size)) | 520 if (!file_util::GetFileSize(TemporaryFileForFilename(filename_), &size)) |
| 561 return OnCorruptDatabase(); | 521 return OnCorruptDatabase(); |
| 562 | 522 |
| 563 // Track update size to answer questions at http://crbug.com/72216 . | 523 // Track update size to answer questions at http://crbug.com/72216 . |
| 564 // Log small updates as 1k so that the 0 (underflow) bucket can be | 524 // Log small updates as 1k so that the 0 (underflow) bucket can be |
| 565 // used for "empty" in SafeBrowsingDatabase. | 525 // used for "empty" in SafeBrowsingDatabase. |
| 566 UMA_HISTOGRAM_COUNTS("SB2.DatabaseUpdateKilobytes", | 526 UMA_HISTOGRAM_COUNTS("SB2.DatabaseUpdateKilobytes", |
| 567 std::max(static_cast<int>(size / 1024), 1)); | 527 std::max(static_cast<int>(size / 1024), 1)); |
| 568 | 528 |
| 569 // TODO(shess): For SB2.AddPrefixesReallocs histogram. | |
| 570 int add_prefixes_reallocs = 0; | |
| 571 | |
| 572 // Append the accumulated chunks onto the vectors read from |file_|. | 529 // Append the accumulated chunks onto the vectors read from |file_|. |
| 573 for (int i = 0; i < chunks_written_; ++i) { | 530 for (int i = 0; i < chunks_written_; ++i) { |
| 574 ChunkHeader header; | 531 ChunkHeader header; |
| 575 | 532 |
| 576 int64 ofs = ftell(new_file_.get()); | 533 int64 ofs = ftell(new_file_.get()); |
| 577 if (ofs == -1) | 534 if (ofs == -1) |
| 578 return false; | 535 return false; |
| 579 | 536 |
| 580 if (!ReadArray(&header, 1, new_file_.get(), NULL)) | 537 if (!ReadItem(&header, new_file_.get(), NULL)) |
| 581 return false; | 538 return false; |
| 582 | 539 |
| 583 // As a safety measure, make sure that the header describes a sane | 540 // As a safety measure, make sure that the header describes a sane |
| 584 // chunk, given the remaining file size. | 541 // chunk, given the remaining file size. |
| 585 int64 expected_size = ofs + sizeof(ChunkHeader); | 542 int64 expected_size = ofs + sizeof(ChunkHeader); |
| 586 expected_size += header.add_prefix_count * sizeof(SBAddPrefix); | 543 expected_size += header.add_prefix_count * sizeof(SBAddPrefix); |
| 587 expected_size += header.sub_prefix_count * sizeof(SBSubPrefix); | 544 expected_size += header.sub_prefix_count * sizeof(SBSubPrefix); |
| 588 expected_size += header.add_hash_count * sizeof(SBAddFullHash); | 545 expected_size += header.add_hash_count * sizeof(SBAddFullHash); |
| 589 expected_size += header.sub_hash_count * sizeof(SBSubFullHash); | 546 expected_size += header.sub_hash_count * sizeof(SBSubFullHash); |
| 590 if (expected_size > size) | 547 if (expected_size > size) |
| 591 return false; | 548 return false; |
| 592 | 549 |
| 593 // TODO(shess): For SB2.AddPrefixesReallocs histogram. | |
| 594 SBAddPrefix* add_prefixes_old_start = | |
| 595 (add_prefixes.size() ? &add_prefixes[0] : NULL); | |
| 596 | |
| 597 // TODO(shess): If the vectors were kept sorted, then this code | 550 // TODO(shess): If the vectors were kept sorted, then this code |
| 598 // could use std::inplace_merge() to merge everything together in | 551 // could use std::inplace_merge() to merge everything together in |
| 599 // sorted order. That might still be slower than just sorting at | 552 // sorted order. That might still be slower than just sorting at |
| 600 // the end if there were a large number of chunks. In that case | 553 // the end if there were a large number of chunks. In that case |
| 601 // some sort of recursive binary merge might be in order (merge | 554 // some sort of recursive binary merge might be in order (merge |
| 602 // chunks pairwise, merge those chunks pairwise, and so on, then | 555 // chunks pairwise, merge those chunks pairwise, and so on, then |
| 603 // merge the result with the main list). | 556 // merge the result with the main list). |
| 604 if (!ReadToVector(&add_prefixes, header.add_prefix_count, | 557 if (!ReadToContainer(&add_prefixes, header.add_prefix_count, |
| 605 new_file_.get(), NULL) || | 558 new_file_.get(), NULL) || |
| 606 !ReadToVector(&sub_prefixes, header.sub_prefix_count, | 559 !ReadToContainer(&sub_prefixes, header.sub_prefix_count, |
| 607 new_file_.get(), NULL) || | 560 new_file_.get(), NULL) || |
| 608 !ReadToVector(&add_full_hashes, header.add_hash_count, | 561 !ReadToContainer(&add_full_hashes, header.add_hash_count, |
| 609 new_file_.get(), NULL) || | 562 new_file_.get(), NULL) || |
| 610 !ReadToVector(&sub_full_hashes, header.sub_hash_count, | 563 !ReadToContainer(&sub_full_hashes, header.sub_hash_count, |
| 611 new_file_.get(), NULL)) | 564 new_file_.get(), NULL)) |
| 612 return false; | 565 return false; |
| 613 | |
| 614 // Determine if the vector data moved to a new location. This | |
| 615 // won't track cases where the malloc library was able to expand | |
| 616 // the storage in place, but those cases shouldn't cause memory | |
| 617 // fragmentation anyhow. | |
| 618 SBAddPrefix* add_prefixes_new_start = | |
| 619 (add_prefixes.size() ? &add_prefixes[0] : NULL); | |
| 620 if (add_prefixes_old_start != add_prefixes_new_start) | |
| 621 ++add_prefixes_reallocs; | |
| 622 } | 566 } |
| 623 | 567 |
| 624 // Track the number of times this number of re-allocations occurred. | |
| 625 UMA_HISTOGRAM_COUNTS_100("SB2.AddPrefixesReallocs", add_prefixes_reallocs); | |
| 626 | |
| 627 // Append items from |pending_adds|. | 568 // Append items from |pending_adds|. |
| 628 add_full_hashes.insert(add_full_hashes.end(), | 569 add_full_hashes.insert(add_full_hashes.end(), |
| 629 pending_adds.begin(), pending_adds.end()); | 570 pending_adds.begin(), pending_adds.end()); |
| 630 | 571 |
| 631 // Check how often a prefix was checked which wasn't in the | 572 // Check how often a prefix was checked which wasn't in the |
| 632 // database. | 573 // database. |
| 633 SBCheckPrefixMisses(add_prefixes, prefix_misses); | 574 SBCheckPrefixMisses(add_prefixes, prefix_misses); |
| 634 | 575 |
| 635 // Knock the subs from the adds and process deleted chunks. | 576 // Knock the subs from the adds and process deleted chunks. |
| 636 SBProcessSubs(&add_prefixes, &sub_prefixes, | 577 SBProcessSubs(&add_prefixes, &sub_prefixes, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 651 // Write a file header. | 592 // Write a file header. |
| 652 FileHeader header; | 593 FileHeader header; |
| 653 header.magic = kFileMagic; | 594 header.magic = kFileMagic; |
| 654 header.version = kFileVersion; | 595 header.version = kFileVersion; |
| 655 header.add_chunk_count = add_chunks_cache_.size(); | 596 header.add_chunk_count = add_chunks_cache_.size(); |
| 656 header.sub_chunk_count = sub_chunks_cache_.size(); | 597 header.sub_chunk_count = sub_chunks_cache_.size(); |
| 657 header.add_prefix_count = add_prefixes.size(); | 598 header.add_prefix_count = add_prefixes.size(); |
| 658 header.sub_prefix_count = sub_prefixes.size(); | 599 header.sub_prefix_count = sub_prefixes.size(); |
| 659 header.add_hash_count = add_full_hashes.size(); | 600 header.add_hash_count = add_full_hashes.size(); |
| 660 header.sub_hash_count = sub_full_hashes.size(); | 601 header.sub_hash_count = sub_full_hashes.size(); |
| 661 if (!WriteArray(&header, 1, new_file_.get(), &context)) | 602 if (!WriteItem(header, new_file_.get(), &context)) |
| 662 return false; | 603 return false; |
| 663 | 604 |
| 664 // Write all the chunk data. | 605 // Write all the chunk data. |
| 665 if (!WriteChunkSet(add_chunks_cache_, new_file_.get(), &context) || | 606 if (!WriteContainer(add_chunks_cache_, new_file_.get(), &context) || |
| 666 !WriteChunkSet(sub_chunks_cache_, new_file_.get(), &context) || | 607 !WriteContainer(sub_chunks_cache_, new_file_.get(), &context) || |
| 667 !WriteVector(add_prefixes, new_file_.get(), &context) || | 608 !WriteContainer(add_prefixes, new_file_.get(), &context) || |
| 668 !WriteVector(sub_prefixes, new_file_.get(), &context) || | 609 !WriteContainer(sub_prefixes, new_file_.get(), &context) || |
| 669 !WriteVector(add_full_hashes, new_file_.get(), &context) || | 610 !WriteContainer(add_full_hashes, new_file_.get(), &context) || |
| 670 !WriteVector(sub_full_hashes, new_file_.get(), &context)) | 611 !WriteContainer(sub_full_hashes, new_file_.get(), &context)) |
| 671 return false; | 612 return false; |
| 672 | 613 |
| 673 // Write the checksum at the end. | 614 // Write the checksum at the end. |
| 674 base::MD5Digest digest; | 615 base::MD5Digest digest; |
| 675 base::MD5Final(&digest, &context); | 616 base::MD5Final(&digest, &context); |
| 676 if (!WriteArray(&digest, 1, new_file_.get(), NULL)) | 617 if (!WriteItem(digest, new_file_.get(), NULL)) |
| 677 return false; | 618 return false; |
| 678 | 619 |
| 679 // Trim any excess left over from the temporary chunk data. | 620 // Trim any excess left over from the temporary chunk data. |
| 680 if (!file_util::TruncateFile(new_file_.get())) | 621 if (!file_util::TruncateFile(new_file_.get())) |
| 681 return false; | 622 return false; |
| 682 | 623 |
| 683 // Close the file handle and swizzle the file into place. | 624 // Close the file handle and swizzle the file into place. |
| 684 new_file_.reset(); | 625 new_file_.reset(); |
| 685 if (!file_util::Delete(filename_, false) && | 626 if (!file_util::Delete(filename_, false) && |
| 686 file_util::PathExists(filename_)) | 627 file_util::PathExists(filename_)) |
| (...skipping 10 matching lines...) Expand all Loading... |
| 697 // Pass the resulting data off to the caller. | 638 // Pass the resulting data off to the caller. |
| 698 add_prefixes_result->swap(add_prefixes); | 639 add_prefixes_result->swap(add_prefixes); |
| 699 add_full_hashes_result->swap(add_full_hashes); | 640 add_full_hashes_result->swap(add_full_hashes); |
| 700 | 641 |
| 701 return true; | 642 return true; |
| 702 } | 643 } |
| 703 | 644 |
| 704 bool SafeBrowsingStoreFile::FinishUpdate( | 645 bool SafeBrowsingStoreFile::FinishUpdate( |
| 705 const std::vector<SBAddFullHash>& pending_adds, | 646 const std::vector<SBAddFullHash>& pending_adds, |
| 706 const std::set<SBPrefix>& prefix_misses, | 647 const std::set<SBPrefix>& prefix_misses, |
| 707 std::vector<SBAddPrefix>* add_prefixes_result, | 648 SBAddPrefixes* add_prefixes_result, |
| 708 std::vector<SBAddFullHash>* add_full_hashes_result) { | 649 std::vector<SBAddFullHash>* add_full_hashes_result) { |
| 709 DCHECK(add_prefixes_result); | 650 DCHECK(add_prefixes_result); |
| 710 DCHECK(add_full_hashes_result); | 651 DCHECK(add_full_hashes_result); |
| 711 | 652 |
| 712 bool ret = DoUpdate(pending_adds, prefix_misses, | 653 bool ret = DoUpdate(pending_adds, prefix_misses, |
| 713 add_prefixes_result, add_full_hashes_result); | 654 add_prefixes_result, add_full_hashes_result); |
| 714 | 655 |
| 715 if (!ret) { | 656 if (!ret) { |
| 716 CancelUpdate(); | 657 CancelUpdate(); |
| 717 return false; | 658 return false; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 753 out->insert(out->end(), sub_chunks_cache_.begin(), sub_chunks_cache_.end()); | 694 out->insert(out->end(), sub_chunks_cache_.begin(), sub_chunks_cache_.end()); |
| 754 } | 695 } |
| 755 | 696 |
| 756 void SafeBrowsingStoreFile::DeleteAddChunk(int32 chunk_id) { | 697 void SafeBrowsingStoreFile::DeleteAddChunk(int32 chunk_id) { |
| 757 add_del_cache_.insert(chunk_id); | 698 add_del_cache_.insert(chunk_id); |
| 758 } | 699 } |
| 759 | 700 |
| 760 void SafeBrowsingStoreFile::DeleteSubChunk(int32 chunk_id) { | 701 void SafeBrowsingStoreFile::DeleteSubChunk(int32 chunk_id) { |
| 761 sub_del_cache_.insert(chunk_id); | 702 sub_del_cache_.insert(chunk_id); |
| 762 } | 703 } |
| OLD | NEW |