1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" 6 7 #include "base/callback.h" 8 #include "base/metrics/histogram.h" 9 #include "base/md5.h" 10 11 namespace { 12 13 // NOTE(shess): kFileMagic should not be a byte-wise palindrome, so 14 // that byte-order changes force corruption. 15 const int32 kFileMagic = 0x600D71FE; 16 const int32 kFileVersion = 7; // SQLite storage was 6... 17 18 // Header at the front of the main database file. 19 struct FileHeader { 20 int32 magic, version; 21 uint32 add_chunk_count, sub_chunk_count; 22 uint32 add_prefix_count, sub_prefix_count; 23 uint32 add_hash_count, sub_hash_count; 24 }; 25 26 // Header for each chunk in the chunk-accumulation file. 27 struct ChunkHeader { 28 uint32 add_prefix_count, sub_prefix_count; 29 uint32 add_hash_count, sub_hash_count; 30 }; 31 32 // Rewind the file. Using fseek(2) because rewind(3) errors are 33 // weird. 34 bool FileRewind(FILE* fp) { 35 int rv = fseek(fp, 0, SEEK_SET); 36 DCHECK_EQ(rv, 0); 37 return rv == 0; 38 } 39 40 // Move file read pointer forward by |bytes| relative to current position. 41 bool FileSkip(size_t bytes, FILE* fp) { 42 // Although fseek takes negative values, for this case, we only want 43 // to skip forward. 44 DCHECK(static_cast<long>(bytes) >= 0); 45 if (static_cast<long>(bytes) < 0) 46 return false; 47 int rv = fseek(fp, static_cast<long>(bytes), SEEK_CUR); 48 DCHECK_EQ(rv, 0); 49 return rv == 0; 50 } 51 52 // Read an array of |nmemb| items from |fp| into |ptr|, and fold the 53 // input data into the checksum in |context|, if non-NULL. Return 54 // true on success. 55 template <class T> 56 bool ReadArray(T* ptr, size_t nmemb, FILE* fp, MD5Context* context) { 57 const size_t ret = fread(ptr, sizeof(T), nmemb, fp); 58 if (ret != nmemb) 59 return false; 60 61 if (context) 62 MD5Update(context, ptr, sizeof(T) * nmemb); 63 return true; 64 } 65 66 // Write an array of |nmemb| items from |ptr| to |fp|, and fold the 67 // output data into the checksum in |context|, if non-NULL. Return 68 // true on success. 69 template <class T> 70 bool WriteArray(const T* ptr, size_t nmemb, FILE* fp, MD5Context* context) { 71 const size_t ret = fwrite(ptr, sizeof(T), nmemb, fp); 72 if (ret != nmemb) 73 return false; 74 75 if (context) 76 MD5Update(context, ptr, sizeof(T) * nmemb); 77 78 return true; 79 } 80 81 // Expand |values| to fit |count| new items, read those items from 82 // |fp| and fold them into the checksum in |context|. Returns true on 83 // success. 84 template <class T> 85 bool ReadToVector(std::vector<T>* values, size_t count, 86 FILE* fp, MD5Context* context) { 87 // Pointers into an empty vector may not be valid. 88 if (!count) 89 return true; 90 91 // Grab the size for purposes of finding where to read to. The 92 // resize could invalidate any iterator captured here. 93 const size_t original_size = values->size(); 94 values->resize(original_size + count); 95 96 // Sayeth Herb Sutter: Vectors are guaranteed to be contiguous. So 97 // get a pointer to where to read the data to. 98 T* ptr = &((*values)[original_size]); 99 if (!ReadArray(ptr, count, fp, context)) { 100 values->resize(original_size); 101 return false; 102 } 103 104 return true; 105 } 106 107 // Write all of |values| to |fp|, and fold the data into the checksum 108 // in |context|, if non-NULL. Returns true on succsess. 109 template <class T> 110 bool WriteVector(const std::vector<T>& values, FILE* fp, MD5Context* context) { 111 // Pointers into empty vectors may not be valid. 112 if (values.empty()) 113 return true; 114 115 // Sayeth Herb Sutter: Vectors are guaranteed to be contiguous. So 116 // get a pointer to where to write from. 117 const T* ptr = &(values[0]); 118 return WriteArray(ptr, values.size(), fp, context); 119 } 120 121 // Read an array of |count| integers and add them to |values|. 122 // Returns true on success. 123 bool ReadToChunkSet(std::set<int32>* values, size_t count, 124 FILE* fp, MD5Context* context) { 125 if (!count) 126 return true; 127 128 std::vector<int32> flat_values; 129 if (!ReadToVector(&flat_values, count, fp, context)) 130 return false; 131 132 values->insert(flat_values.begin(), flat_values.end()); 133 return true; 134 } 135 136 // Write the contents of |values| as an array of integers. Returns 137 // true on success. 138 bool WriteChunkSet(const std::set<int32>& values, 139 FILE* fp, MD5Context* context) { 140 if (values.empty()) 141 return true; 142 143 const std::vector<int32> flat_values(values.begin(), values.end()); 144 return WriteVector(flat_values, fp, context); 145 } 146 147 // Delete the chunks in |deleted| from |chunks|. 148 void DeleteChunksFromSet(const base::hash_set<int32>& deleted, 149 std::set<int32>* chunks) { 150 for (std::set<int32>::iterator iter = chunks->begin(); 151 iter != chunks->end();) { 152 std::set<int32>::iterator prev = iter++; 153 if (deleted.count(*prev) > 0) 154 chunks->erase(prev); 155 } 156 } 157 158 // Sanity-check the header against the file's size to make sure our 159 // vectors aren't gigantic. This doubles as a cheap way to detect 160 // corruption without having to checksum the entire file. 161 bool FileHeaderSanityCheck(const FilePath& filename, 162 const FileHeader& header) { 163 int64 size = 0; 164 if (!file_util::GetFileSize(filename, &size)) 165 return false; 166 167 int64 expected_size = sizeof(FileHeader); 168 expected_size += header.add_chunk_count * sizeof(int32); 169 expected_size += header.sub_chunk_count * sizeof(int32); 170 expected_size += header.add_prefix_count * sizeof(SBAddPrefix); 171 expected_size += header.sub_prefix_count * sizeof(SBSubPrefix); 172 expected_size += header.add_hash_count * sizeof(SBAddFullHash); 173 expected_size += header.sub_hash_count * sizeof(SBSubFullHash); 174 expected_size += sizeof(MD5Digest); 175 if (size != expected_size) 176 return false; 177 178 return true; 179 } 180 181 // This a helper function that reads header to |header|. Returns true if the 182 // magic number is correct and santiy check passes. 183 bool ReadAndVerifyHeader(const FilePath& filename, 184 FILE* fp, 185 FileHeader* header, 186 MD5Context* context) { 187 if (!ReadArray(header, 1, fp, context)) 188 return false; 189 if (header->magic != kFileMagic || header->version != kFileVersion) 190 return false; 191 if (!FileHeaderSanityCheck(filename, *header)) 192 return false; 193 return true; 194 } 195 196 } // namespace 197 198 // static 199 void SafeBrowsingStoreFile::RecordFormatEvent(FormatEventType event_type) { 200 UMA_HISTOGRAM_ENUMERATION("SB2.FormatEvent", event_type, FORMAT_EVENT_MAX); 201 } 202 203 // static 204 void SafeBrowsingStoreFile::CheckForOriginalAndDelete( 205 const FilePath& current_filename) { 206 const FilePath original_filename( 207 current_filename.DirName().AppendASCII("Safe Browsing")); 208 if (file_util::PathExists(original_filename)) { 209 int64 size = 0; 210 if (file_util::GetFileSize(original_filename, &size)) { 211 UMA_HISTOGRAM_COUNTS("SB2.OldDatabaseKilobytes", 212 static_cast<int>(size / 1024)); 213 } 214 215 if (file_util::Delete(original_filename, false)) { 216 RecordFormatEvent(FORMAT_EVENT_DELETED_ORIGINAL); 217 } else { 218 RecordFormatEvent(FORMAT_EVENT_DELETED_ORIGINAL_FAILED); 219 } 220 221 // Just best-effort on the journal file, don't want to get lost in 222 // the weeds. 223 const FilePath journal_filename( 224 current_filename.DirName().AppendASCII("Safe Browsing-journal")); 225 file_util::Delete(journal_filename, false); 226 } 227 } 228 229 SafeBrowsingStoreFile::SafeBrowsingStoreFile() 230 : chunks_written_(0), 231 file_(NULL), 232 empty_(false), 233 corruption_seen_(false) { 234 } 235 236 SafeBrowsingStoreFile::~SafeBrowsingStoreFile() { 237 Close(); 238 } 239 240 bool SafeBrowsingStoreFile::Delete() { 241 // The database should not be open at this point. But, just in 242 // case, close everything before deleting. 243 if (!Close()) { 244 NOTREACHED(); 245 return false; 246 } 247 248 if (!file_util::Delete(filename_, false) && 249 file_util::PathExists(filename_)) { 250 NOTREACHED(); 251 return false; 252 } 253 254 const FilePath new_filename = TemporaryFileForFilename(filename_); 255 if (!file_util::Delete(new_filename, false) && 256 file_util::PathExists(new_filename)) { 257 NOTREACHED(); 258 return false; 259 } 260 261 // With SQLite support gone, one way to get to this code is if the 262 // existing file is a SQLite file. Make sure the journal file is 263 // also removed. 264 const FilePath journal_filename( 265 filename_.value() + FILE_PATH_LITERAL("-journal")); 266 if (file_util::PathExists(journal_filename)) 267 file_util::Delete(journal_filename, false); 268 269 return true; 270 } 271 272 void SafeBrowsingStoreFile::Init(const FilePath& filename, 273 Callback0::Type* corruption_callback) { 274 filename_ = filename; 275 corruption_callback_.reset(corruption_callback); 276 } 277 278 bool SafeBrowsingStoreFile::BeginChunk() { 279 return ClearChunkBuffers(); 280 } 281 282 bool SafeBrowsingStoreFile::WriteAddPrefix(int32 chunk_id, SBPrefix prefix) { 283 add_prefixes_.push_back(SBAddPrefix(chunk_id, prefix)); 284 return true; 285 } 286 287 bool SafeBrowsingStoreFile::GetAddPrefixes( 288 std::vector<SBAddPrefix>* add_prefixes) { 289 add_prefixes->clear(); 290 291 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); 292 if (file.get() == NULL) return false; 293 294 FileHeader header; 295 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) 296 return OnCorruptDatabase(); 297 298 size_t add_prefix_offset = header.add_chunk_count * sizeof(int32) + 299 header.sub_chunk_count * sizeof(int32); 300 if (!FileSkip(add_prefix_offset, file.get())) 301 return false; 302 303 if (!ReadToVector(add_prefixes, header.add_prefix_count, file.get(), NULL)) 304 return false; 305 306 return true; 307 } 308 309 bool SafeBrowsingStoreFile::GetAddFullHashes( 310 std::vector<SBAddFullHash>* add_full_hashes) { 311 add_full_hashes->clear(); 312 313 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); 314 if (file.get() == NULL) return false; 315 316 FileHeader header; 317 if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) 318 return OnCorruptDatabase(); 319 320 size_t offset = 321 header.add_chunk_count * sizeof(int32) + 322 header.sub_chunk_count * sizeof(int32) + 323 header.add_prefix_count * sizeof(SBAddPrefix) + 324 header.sub_prefix_count * sizeof(SBSubPrefix); 325 if (!FileSkip(offset, file.get())) 326 return false; 327 328 return ReadToVector(add_full_hashes, 329 header.add_hash_count, 330 file.get(), 331 NULL); 332 } 333 334 bool SafeBrowsingStoreFile::WriteAddHash(int32 chunk_id, 335 base::Time receive_time, 336 const SBFullHash& full_hash) { 337 add_hashes_.push_back(SBAddFullHash(chunk_id, receive_time, full_hash)); 338 return true; 339 } 340 341 bool SafeBrowsingStoreFile::WriteSubPrefix(int32 chunk_id, 342 int32 add_chunk_id, 343 SBPrefix prefix) { 344 sub_prefixes_.push_back(SBSubPrefix(chunk_id, add_chunk_id, prefix)); 345 return true; 346 } 347 348 bool SafeBrowsingStoreFile::WriteSubHash(int32 chunk_id, int32 add_chunk_id, 349 const SBFullHash& full_hash) { 350 sub_hashes_.push_back(SBSubFullHash(chunk_id, add_chunk_id, full_hash)); 351 return true; 352 } 353 354 bool SafeBrowsingStoreFile::OnCorruptDatabase() { 355 if (!corruption_seen_) 356 RecordFormatEvent(FORMAT_EVENT_FILE_CORRUPT); 357 corruption_seen_ = true; 358 359 if (corruption_callback_.get()) 360 corruption_callback_->Run(); 361 362 // Return false as a convenience to callers. 363 return false; 364 } 365 366 bool SafeBrowsingStoreFile::Close() { 367 ClearUpdateBuffers(); 368 369 // Make sure the files are closed. 370 file_.reset(); 371 new_file_.reset(); 372 return true; 373 } 374 375 bool SafeBrowsingStoreFile::BeginUpdate() { 376 DCHECK(!file_.get() && !new_file_.get()); 377 378 // Structures should all be clear unless something bad happened. 379 DCHECK(add_chunks_cache_.empty()); 380 DCHECK(sub_chunks_cache_.empty()); 381 DCHECK(add_del_cache_.empty()); 382 DCHECK(sub_del_cache_.empty()); 383 DCHECK(add_prefixes_.empty()); 384 DCHECK(sub_prefixes_.empty()); 385 DCHECK(add_hashes_.empty()); 386 DCHECK(sub_hashes_.empty()); 387 DCHECK_EQ(chunks_written_, 0); 388 389 // Since the following code will already hit the profile looking for 390 // database files, this is a reasonable to time delete any old 391 // files. 392 CheckForOriginalAndDelete(filename_); 393 394 corruption_seen_ = false; 395 396 const FilePath new_filename = TemporaryFileForFilename(filename_); 397 file_util::ScopedFILE new_file(file_util::OpenFile(new_filename, "wb+")); 398 if (new_file.get() == NULL) 399 return false; 400 401 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); 402 empty_ = (file.get() == NULL); 403 if (empty_) { 404 // If the file exists but cannot be opened, try to delete it (not 405 // deleting directly, the bloom filter needs to be deleted, too). 406 if (file_util::PathExists(filename_)) 407 return OnCorruptDatabase(); 408 409 new_file_.swap(new_file); 410 return true; 411 } 412 413 FileHeader header; 414 if (!ReadArray(&header, 1, file.get(), NULL)) 415 return OnCorruptDatabase(); 416 417 if (header.magic != kFileMagic || header.version != kFileVersion) { 418 if (!strcmp(reinterpret_cast<char*>(&header.magic), "SQLite format 3")) { 419 RecordFormatEvent(FORMAT_EVENT_FOUND_SQLITE); 420 } else { 421 RecordFormatEvent(FORMAT_EVENT_FOUND_UNKNOWN); 422 } 423 424 // Close the file so that it can be deleted. 425 file.reset(); 426 427 return OnCorruptDatabase(); 428 } 429 430 // TODO(shess): Under POSIX it is possible that this could size a 431 // file different from the file which was opened. 432 if (!FileHeaderSanityCheck(filename_, header)) 433 return OnCorruptDatabase(); 434 435 // Pull in the chunks-seen data for purposes of implementing 436 // |GetAddChunks()| and |GetSubChunks()|. This data is sent up to 437 // the server at the beginning of an update. 438 if (!ReadToChunkSet(&add_chunks_cache_, header.add_chunk_count, 439 file.get(), NULL) || 440 !ReadToChunkSet(&sub_chunks_cache_, header.sub_chunk_count, 441 file.get(), NULL)) 442 return OnCorruptDatabase(); 443 444 file_.swap(file); 445 new_file_.swap(new_file); 446 return true; 447 } 448 449 bool SafeBrowsingStoreFile::FinishChunk() { 450 if (!add_prefixes_.size() && !sub_prefixes_.size() && 451 !add_hashes_.size() && !sub_hashes_.size()) 452 return true; 453 454 ChunkHeader header; 455 header.add_prefix_count = add_prefixes_.size(); 456 header.sub_prefix_count = sub_prefixes_.size(); 457 header.add_hash_count = add_hashes_.size(); 458 header.sub_hash_count = sub_hashes_.size(); 459 if (!WriteArray(&header, 1, new_file_.get(), NULL)) 460 return false; 461 462 if (!WriteVector(add_prefixes_, new_file_.get(), NULL) || 463 !WriteVector(sub_prefixes_, new_file_.get(), NULL) || 464 !WriteVector(add_hashes_, new_file_.get(), NULL) || 465 !WriteVector(sub_hashes_, new_file_.get(), NULL)) 466 return false; 467 468 ++chunks_written_; 469 470 // Clear everything to save memory. 471 return ClearChunkBuffers(); 472 } 473 474 bool SafeBrowsingStoreFile::DoUpdate( 475 const std::vector<SBAddFullHash>& pending_adds, 476 const std::set<SBPrefix>& prefix_misses, 477 std::vector<SBAddPrefix>* add_prefixes_result, 478 std::vector<SBAddFullHash>* add_full_hashes_result) { 479 DCHECK(file_.get() || empty_); 480 DCHECK(new_file_.get()); 481 CHECK(add_prefixes_result); 482 CHECK(add_full_hashes_result); 483 484 std::vector<SBAddPrefix> add_prefixes; 485 std::vector<SBSubPrefix> sub_prefixes; 486 std::vector<SBAddFullHash> add_full_hashes; 487 std::vector<SBSubFullHash> sub_full_hashes; 488 489 // Read original data into the vectors. 490 if (!empty_) { 491 DCHECK(file_.get()); 492 493 if (!FileRewind(file_.get())) 494 return OnCorruptDatabase(); 495 496 MD5Context context; 497 MD5Init(&context); 498 499 // Read the file header and make sure it looks right. 500 FileHeader header; 501 if (!ReadAndVerifyHeader(filename_, file_.get(), &header, &context)) 502 return OnCorruptDatabase(); 503 504 // Re-read the chunks-seen data to get to the later data in the 505 // file and calculate the checksum. No new elements should be 506 // added to the sets. 507 if (!ReadToChunkSet(&add_chunks_cache_, header.add_chunk_count, 508 file_.get(), &context) || 509 !ReadToChunkSet(&sub_chunks_cache_, header.sub_chunk_count, 510 file_.get(), &context)) 511 return OnCorruptDatabase(); 512 513 if (!ReadToVector(&add_prefixes, header.add_prefix_count, 514 file_.get(), &context) || 515 !ReadToVector(&sub_prefixes, header.sub_prefix_count, 516 file_.get(), &context) || 517 !ReadToVector(&add_full_hashes, header.add_hash_count, 518 file_.get(), &context) || 519 !ReadToVector(&sub_full_hashes, header.sub_hash_count, 520 file_.get(), &context)) 521 return OnCorruptDatabase(); 522 523 // Calculate the digest to this point. 524 MD5Digest calculated_digest; 525 MD5Final(&calculated_digest, &context); 526 527 // Read the stored checksum and verify it. 528 MD5Digest file_digest; 529 if (!ReadArray(&file_digest, 1, file_.get(), NULL)) 530 return OnCorruptDatabase(); 531 532 if (0 != memcmp(&file_digest, &calculated_digest, sizeof(file_digest))) 533 return OnCorruptDatabase(); 534 535 // Close the file so we can later rename over it. 536 file_.reset(); 537 } 538 DCHECK(!file_.get()); 539 540 // Rewind the temporary storage. 541 if (!FileRewind(new_file_.get())) 542 return false; 543 544 // Get chunk file's size for validating counts. 545 int64 size = 0; 546 if (!file_util::GetFileSize(TemporaryFileForFilename(filename_), &size)) 547 return OnCorruptDatabase(); 548 549 // Track update size to answer questions at http://crbug.com/72216 . 550 // Log small updates as 1k so that the 0 (underflow) bucket can be 551 // used for "empty" in SafeBrowsingDatabase. 552 UMA_HISTOGRAM_COUNTS("SB2.DatabaseUpdateKilobytes", 553 std::max(static_cast<int>(size / 1024), 1)); 554 555 // Append the accumulated chunks onto the vectors read from |file_|. 556 for (int i = 0; i < chunks_written_; ++i) { 557 ChunkHeader header; 558 559 int64 ofs = ftell(new_file_.get()); 560 if (ofs == -1) 561 return false; 562 563 if (!ReadArray(&header, 1, new_file_.get(), NULL)) 564 return false; 565 566 // As a safety measure, make sure that the header describes a sane 567 // chunk, given the remaining file size. 568 int64 expected_size = ofs + sizeof(ChunkHeader); 569 expected_size += header.add_prefix_count * sizeof(SBAddPrefix); 570 expected_size += header.sub_prefix_count * sizeof(SBSubPrefix); 571 expected_size += header.add_hash_count * sizeof(SBAddFullHash); 572 expected_size += header.sub_hash_count * sizeof(SBSubFullHash); 573 if (expected_size > size) 574 return false; 575 576 // TODO(shess): If the vectors were kept sorted, then this code 577 // could use std::inplace_merge() to merge everything together in 578 // sorted order. That might still be slower than just sorting at 579 // the end if there were a large number of chunks. In that case 580 // some sort of recursive binary merge might be in order (merge 581 // chunks pairwise, merge those chunks pairwise, and so on, then 582 // merge the result with the main list). 583 if (!ReadToVector(&add_prefixes, header.add_prefix_count, 584 new_file_.get(), NULL) || 585 !ReadToVector(&sub_prefixes, header.sub_prefix_count, 586 new_file_.get(), NULL) || 587 !ReadToVector(&add_full_hashes, header.add_hash_count, 588 new_file_.get(), NULL) || 589 !ReadToVector(&sub_full_hashes, header.sub_hash_count, 590 new_file_.get(), NULL)) 591 return false; 592 } 593 594 // Append items from |pending_adds|. 595 add_full_hashes.insert(add_full_hashes.end(), 596 pending_adds.begin(), pending_adds.end()); 597 598 // Check how often a prefix was checked which wasn't in the 599 // database. 600 SBCheckPrefixMisses(add_prefixes, prefix_misses); 601 602 // Knock the subs from the adds and process deleted chunks. 603 SBProcessSubs(&add_prefixes, &sub_prefixes, 604 &add_full_hashes, &sub_full_hashes, 605 add_del_cache_, sub_del_cache_); 606 607 // We no longer need to track deleted chunks. 608 DeleteChunksFromSet(add_del_cache_, &add_chunks_cache_); 609 DeleteChunksFromSet(sub_del_cache_, &sub_chunks_cache_); 610 611 // Write the new data to new_file_. 612 if (!FileRewind(new_file_.get())) 613 return false; 614 615 MD5Context context; 616 MD5Init(&context); 617 618 // Write a file header. 619 FileHeader header; 620 header.magic = kFileMagic; 621 header.version = kFileVersion; 622 header.add_chunk_count = add_chunks_cache_.size(); 623 header.sub_chunk_count = sub_chunks_cache_.size(); 624 header.add_prefix_count = add_prefixes.size(); 625 header.sub_prefix_count = sub_prefixes.size(); 626 header.add_hash_count = add_full_hashes.size(); 627 header.sub_hash_count = sub_full_hashes.size(); 628 if (!WriteArray(&header, 1, new_file_.get(), &context)) 629 return false; 630 631 // Write all the chunk data. 632 if (!WriteChunkSet(add_chunks_cache_, new_file_.get(), &context) || 633 !WriteChunkSet(sub_chunks_cache_, new_file_.get(), &context) || 634 !WriteVector(add_prefixes, new_file_.get(), &context) || 635 !WriteVector(sub_prefixes, new_file_.get(), &context) || 636 !WriteVector(add_full_hashes, new_file_.get(), &context) || 637 !WriteVector(sub_full_hashes, new_file_.get(), &context)) 638 return false; 639 640 // Write the checksum at the end. 641 MD5Digest digest; 642 MD5Final(&digest, &context); 643 if (!WriteArray(&digest, 1, new_file_.get(), NULL)) 644 return false; 645 646 // Trim any excess left over from the temporary chunk data. 647 if (!file_util::TruncateFile(new_file_.get())) 648 return false; 649 650 // Close the file handle and swizzle the file into place. 651 new_file_.reset(); 652 if (!file_util::Delete(filename_, false) && 653 file_util::PathExists(filename_)) 654 return false; 655 656 const FilePath new_filename = TemporaryFileForFilename(filename_); 657 if (!file_util::Move(new_filename, filename_)) 658 return false; 659 660 // Record counts before swapping to caller. 661 UMA_HISTOGRAM_COUNTS("SB2.AddPrefixes", add_prefixes.size()); 662 UMA_HISTOGRAM_COUNTS("SB2.SubPrefixes", sub_prefixes.size()); 663 664 // Pass the resulting data off to the caller. 665 add_prefixes_result->swap(add_prefixes); 666 add_full_hashes_result->swap(add_full_hashes); 667 668 return true; 669 } 670 671 bool SafeBrowsingStoreFile::FinishUpdate( 672 const std::vector<SBAddFullHash>& pending_adds, 673 const std::set<SBPrefix>& prefix_misses, 674 std::vector<SBAddPrefix>* add_prefixes_result, 675 std::vector<SBAddFullHash>* add_full_hashes_result) { 676 DCHECK(add_prefixes_result); 677 DCHECK(add_full_hashes_result); 678 679 bool ret = DoUpdate(pending_adds, prefix_misses, 680 add_prefixes_result, add_full_hashes_result); 681 682 if (!ret) { 683 CancelUpdate(); 684 return false; 685 } 686 687 DCHECK(!new_file_.get()); 688 DCHECK(!file_.get()); 689 690 return Close(); 691 } 692 693 bool SafeBrowsingStoreFile::CancelUpdate() { 694 return Close(); 695 } 696 697 void SafeBrowsingStoreFile::SetAddChunk(int32 chunk_id) { 698 add_chunks_cache_.insert(chunk_id); 699 } 700 701 bool SafeBrowsingStoreFile::CheckAddChunk(int32 chunk_id) { 702 return add_chunks_cache_.count(chunk_id) > 0; 703 } 704 705 void SafeBrowsingStoreFile::GetAddChunks(std::vector<int32>* out) { 706 out->clear(); 707 out->insert(out->end(), add_chunks_cache_.begin(), add_chunks_cache_.end()); 708 } 709 710 void SafeBrowsingStoreFile::SetSubChunk(int32 chunk_id) { 711 sub_chunks_cache_.insert(chunk_id); 712 } 713 714 bool SafeBrowsingStoreFile::CheckSubChunk(int32 chunk_id) { 715 return sub_chunks_cache_.count(chunk_id) > 0; 716 } 717 718 void SafeBrowsingStoreFile::GetSubChunks(std::vector<int32>* out) { 719 out->clear(); 720 out->insert(out->end(), sub_chunks_cache_.begin(), sub_chunks_cache_.end()); 721 } 722 723 void SafeBrowsingStoreFile::DeleteAddChunk(int32 chunk_id) { 724 add_del_cache_.insert(chunk_id); 725 } 726 727 void SafeBrowsingStoreFile::DeleteSubChunk(int32 chunk_id) { 728 sub_del_cache_.insert(chunk_id); 729 } 730