OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/files/scoped_file.h" | 8 #include "base/files/scoped_file.h" |
9 #include "base/md5.h" | 9 #include "base/md5.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 | 79 |
80 // Enumerate different format-change events for histogramming | 80 // Enumerate different format-change events for histogramming |
81 // purposes. DO NOT CHANGE THE ORDERING OF THESE VALUES. | 81 // purposes. DO NOT CHANGE THE ORDERING OF THESE VALUES. |
82 enum FormatEventType { | 82 enum FormatEventType { |
83 // Corruption detected, broken down by file format. | 83 // Corruption detected, broken down by file format. |
84 FORMAT_EVENT_FILE_CORRUPT, | 84 FORMAT_EVENT_FILE_CORRUPT, |
85 FORMAT_EVENT_SQLITE_CORRUPT, // Obsolete | 85 FORMAT_EVENT_SQLITE_CORRUPT, // Obsolete |
86 | 86 |
87 // The type of format found in the file. The expected case (new | 87 // The type of format found in the file. The expected case (new |
88 // file format) is intentionally not covered. | 88 // file format) is intentionally not covered. |
89 FORMAT_EVENT_FOUND_SQLITE, | 89 FORMAT_EVENT_FOUND_SQLITE, // Obsolete |
90 FORMAT_EVENT_FOUND_UNKNOWN, | 90 FORMAT_EVENT_FOUND_UNKNOWN, // magic does not match. |
91 | 91 |
92 // The number of SQLite-format files deleted should be the same as | 92 // The number of SQLite-format files deleted should be the same as |
93 // FORMAT_EVENT_FOUND_SQLITE. It can differ if the delete fails, | 93 // FORMAT_EVENT_FOUND_SQLITE. It can differ if the delete fails, |
94 // or if a failure prevents the update from succeeding. | 94 // or if a failure prevents the update from succeeding. |
95 FORMAT_EVENT_SQLITE_DELETED, // Obsolete | 95 FORMAT_EVENT_SQLITE_DELETED, // Obsolete |
96 FORMAT_EVENT_SQLITE_DELETE_FAILED, // Obsolete | 96 FORMAT_EVENT_SQLITE_DELETE_FAILED, // Obsolete |
97 | 97 |
98 // Found and deleted (or failed to delete) the ancient "Safe | 98 // Found and deleted (or failed to delete) the ancient "Safe |
99 // Browsing" file. | 99 // Browsing" file. |
100 FORMAT_EVENT_DELETED_ORIGINAL, | 100 FORMAT_EVENT_DELETED_ORIGINAL, // Obsolete |
101 FORMAT_EVENT_DELETED_ORIGINAL_FAILED, | 101 FORMAT_EVENT_DELETED_ORIGINAL_FAILED, // Obsolete |
102 | 102 |
103 // The checksum did not check out in CheckValidity() or in | 103 // The checksum did not check out in CheckValidity() or in |
104 // FinishUpdate(). This most likely indicates that the machine | 104 // FinishUpdate(). This most likely indicates that the machine |
105 // crashed before the file was fully sync'ed to disk. | 105 // crashed before the file was fully sync'ed to disk. |
106 FORMAT_EVENT_VALIDITY_CHECKSUM_FAILURE, | 106 FORMAT_EVENT_VALIDITY_CHECKSUM_FAILURE, |
107 FORMAT_EVENT_UPDATE_CHECKSUM_FAILURE, | 107 FORMAT_EVENT_UPDATE_CHECKSUM_FAILURE, |
108 | 108 |
109 // The header checksum was incorrect in ReadAndVerifyHeader(). Likely | 109 // The header checksum was incorrect in ReadAndVerifyHeader(). Likely |
110 // indicates that the system crashed while writing an update. | 110 // indicates that the system crashed while writing an update. |
111 FORMAT_EVENT_HEADER_CHECKSUM_FAILURE, | 111 FORMAT_EVENT_HEADER_CHECKSUM_FAILURE, |
112 | 112 |
| 113 FORMAT_EVENT_FOUND_DEPRECATED, // version too old. |
| 114 |
113 // Memory space for histograms is determined by the max. ALWAYS | 115 // Memory space for histograms is determined by the max. ALWAYS |
114 // ADD NEW VALUES BEFORE THIS ONE. | 116 // ADD NEW VALUES BEFORE THIS ONE. |
115 FORMAT_EVENT_MAX | 117 FORMAT_EVENT_MAX |
116 }; | 118 }; |
117 | 119 |
118 void RecordFormatEvent(FormatEventType event_type) { | 120 void RecordFormatEvent(FormatEventType event_type) { |
119 UMA_HISTOGRAM_ENUMERATION("SB2.FormatEvent", event_type, FORMAT_EVENT_MAX); | 121 UMA_HISTOGRAM_ENUMERATION("SB2.FormatEvent", event_type, FORMAT_EVENT_MAX); |
120 } | 122 } |
121 | 123 |
122 // Rewind the file. Using fseek(2) because rewind(3) errors are | 124 // Rewind the file. Using fseek(2) because rewind(3) errors are |
(...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
623 | 625 |
624 int64 size = 0; | 626 int64 size = 0; |
625 if (!base::GetFileSize(filename, &size)) | 627 if (!base::GetFileSize(filename, &size)) |
626 return false; | 628 return false; |
627 | 629 |
628 return static_cast<int64>(ftell(file.get())) == size; | 630 return static_cast<int64>(ftell(file.get())) == size; |
629 } | 631 } |
630 | 632 |
631 } // namespace | 633 } // namespace |
632 | 634 |
633 // static | |
634 void SafeBrowsingStoreFile::CheckForOriginalAndDelete( | |
635 const base::FilePath& current_filename) { | |
636 const base::FilePath original_filename( | |
637 current_filename.DirName().AppendASCII("Safe Browsing")); | |
638 if (base::PathExists(original_filename)) { | |
639 int64 size = 0; | |
640 if (base::GetFileSize(original_filename, &size)) { | |
641 UMA_HISTOGRAM_COUNTS("SB2.OldDatabaseKilobytes", | |
642 static_cast<int>(size / 1024)); | |
643 } | |
644 | |
645 if (base::DeleteFile(original_filename, false)) { | |
646 RecordFormatEvent(FORMAT_EVENT_DELETED_ORIGINAL); | |
647 } else { | |
648 RecordFormatEvent(FORMAT_EVENT_DELETED_ORIGINAL_FAILED); | |
649 } | |
650 | |
651 // Just best-effort on the journal file, don't want to get lost in | |
652 // the weeds. | |
653 const base::FilePath journal_filename( | |
654 current_filename.DirName().AppendASCII("Safe Browsing-journal")); | |
655 base::DeleteFile(journal_filename, false); | |
656 } | |
657 } | |
658 | |
659 SafeBrowsingStoreFile::SafeBrowsingStoreFile() | 635 SafeBrowsingStoreFile::SafeBrowsingStoreFile() |
660 : chunks_written_(0), empty_(false), corruption_seen_(false) {} | 636 : chunks_written_(0), empty_(false), corruption_seen_(false) {} |
661 | 637 |
662 SafeBrowsingStoreFile::~SafeBrowsingStoreFile() { | 638 SafeBrowsingStoreFile::~SafeBrowsingStoreFile() { |
663 Close(); | 639 Close(); |
664 } | 640 } |
665 | 641 |
666 bool SafeBrowsingStoreFile::Delete() { | 642 bool SafeBrowsingStoreFile::Delete() { |
667 // The database should not be open at this point. But, just in | 643 // The database should not be open at this point. But, just in |
668 // case, close everything before deleting. | 644 // case, close everything before deleting. |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
810 DCHECK(add_chunks_cache_.empty()); | 786 DCHECK(add_chunks_cache_.empty()); |
811 DCHECK(sub_chunks_cache_.empty()); | 787 DCHECK(sub_chunks_cache_.empty()); |
812 DCHECK(add_del_cache_.empty()); | 788 DCHECK(add_del_cache_.empty()); |
813 DCHECK(sub_del_cache_.empty()); | 789 DCHECK(sub_del_cache_.empty()); |
814 DCHECK(add_prefixes_.empty()); | 790 DCHECK(add_prefixes_.empty()); |
815 DCHECK(sub_prefixes_.empty()); | 791 DCHECK(sub_prefixes_.empty()); |
816 DCHECK(add_hashes_.empty()); | 792 DCHECK(add_hashes_.empty()); |
817 DCHECK(sub_hashes_.empty()); | 793 DCHECK(sub_hashes_.empty()); |
818 DCHECK_EQ(chunks_written_, 0); | 794 DCHECK_EQ(chunks_written_, 0); |
819 | 795 |
820 // Since the following code will already hit the profile looking for | |
821 // database files, this is a reasonable to time delete any old | |
822 // files. | |
823 CheckForOriginalAndDelete(filename_); | |
824 | |
825 corruption_seen_ = false; | 796 corruption_seen_ = false; |
826 | 797 |
827 const base::FilePath new_filename = TemporaryFileForFilename(filename_); | 798 const base::FilePath new_filename = TemporaryFileForFilename(filename_); |
828 base::ScopedFILE new_file(base::OpenFile(new_filename, "wb+")); | 799 base::ScopedFILE new_file(base::OpenFile(new_filename, "wb+")); |
829 if (new_file.get() == NULL) | 800 if (new_file.get() == NULL) |
830 return false; | 801 return false; |
831 | 802 |
832 base::ScopedFILE file(base::OpenFile(filename_, "rb")); | 803 base::ScopedFILE file(base::OpenFile(filename_, "rb")); |
833 empty_ = (file.get() == NULL); | 804 empty_ = (file.get() == NULL); |
834 if (empty_) { | 805 if (empty_) { |
835 // If the file exists but cannot be opened, try to delete it (not | 806 // If the file exists but cannot be opened, try to delete it (not |
836 // deleting directly, the bloom filter needs to be deleted, too). | 807 // deleting directly, the bloom filter needs to be deleted, too). |
837 if (base::PathExists(filename_)) | 808 if (base::PathExists(filename_)) |
838 return OnCorruptDatabase(); | 809 return OnCorruptDatabase(); |
839 | 810 |
840 new_file_.swap(new_file); | 811 new_file_.swap(new_file); |
841 return true; | 812 return true; |
842 } | 813 } |
843 | 814 |
844 base::MD5Context context; | 815 base::MD5Context context; |
845 FileHeader header; | 816 FileHeader header; |
846 const int version = | 817 const int version = |
847 ReadAndVerifyHeader(filename_, &header, | 818 ReadAndVerifyHeader(filename_, &header, |
848 &add_chunks_cache_, &sub_chunks_cache_, | 819 &add_chunks_cache_, &sub_chunks_cache_, |
849 file.get(), &context); | 820 file.get(), &context); |
850 if (version == kInvalidVersion) { | 821 if (version == kInvalidVersion) { |
851 FileHeaderV8 retry_header; | 822 FileHeaderV8 retry_header; |
852 if (FileRewind(file.get()) && ReadItem(&retry_header, file.get(), NULL) && | 823 if (FileRewind(file.get()) && ReadItem(&retry_header, file.get(), NULL)) { |
853 (retry_header.magic != kFileMagic || | 824 if (retry_header.magic == kFileMagic && retry_header.version < 7) { |
854 (retry_header.version != 8 && retry_header.version != 7))) { | 825 RecordFormatEvent(FORMAT_EVENT_FOUND_DEPRECATED); |
855 // TODO(shess): Think on whether these histograms are generating any | |
856 // actionable data. I kid you not, SQLITE happens many thousands of times | |
857 // per day, UNKNOWN about 3x higher than that. | |
858 if (!strcmp(reinterpret_cast<char*>(&retry_header.magic), | |
859 "SQLite format 3")) { | |
860 RecordFormatEvent(FORMAT_EVENT_FOUND_SQLITE); | |
861 } else { | 826 } else { |
862 RecordFormatEvent(FORMAT_EVENT_FOUND_UNKNOWN); | 827 RecordFormatEvent(FORMAT_EVENT_FOUND_UNKNOWN); |
863 } | 828 } |
864 } | 829 } |
865 | 830 |
866 // Close the file so that it can be deleted. | 831 // Close the file so that it can be deleted. |
867 file.reset(); | 832 file.reset(); |
868 | 833 |
869 return OnCorruptDatabase(); | 834 return OnCorruptDatabase(); |
870 } | 835 } |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1243 // With SQLite support gone, one way to get to this code is if the | 1208 // With SQLite support gone, one way to get to this code is if the |
1244 // existing file is a SQLite file. Make sure the journal file is | 1209 // existing file is a SQLite file. Make sure the journal file is |
1245 // also removed. | 1210 // also removed. |
1246 const base::FilePath journal_filename( | 1211 const base::FilePath journal_filename( |
1247 basename.value() + FILE_PATH_LITERAL("-journal")); | 1212 basename.value() + FILE_PATH_LITERAL("-journal")); |
1248 if (base::PathExists(journal_filename)) | 1213 if (base::PathExists(journal_filename)) |
1249 base::DeleteFile(journal_filename, false); | 1214 base::DeleteFile(journal_filename, false); |
1250 | 1215 |
1251 return true; | 1216 return true; |
1252 } | 1217 } |
OLD | NEW |