| OLD | NEW | 
|---|
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 
| 2 // | 2 // | 
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. | 
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at | 
| 6 // | 6 // | 
| 7 //     http://www.apache.org/licenses/LICENSE-2.0 | 7 //     http://www.apache.org/licenses/LICENSE-2.0 | 
| 8 // | 8 // | 
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software | 
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, | 
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and | 
| 13 // limitations under the License. | 13 // limitations under the License. | 
| 14 | 14 | 
| 15 #include "client/crash_report_database.h" | 15 #include "client/crash_report_database.h" | 
| 16 | 16 | 
| 17 #include <rpc.h> | 17 #include <rpc.h> | 
| 18 #include <string.h> | 18 #include <string.h> | 
| 19 #include <time.h> | 19 #include <time.h> | 
| 20 #include <windows.h> | 20 #include <windows.h> | 
| 21 | 21 | 
| 22 #include "base/logging.h" | 22 #include "base/logging.h" | 
| 23 #include "base/numerics/safe_math.h" | 23 #include "base/numerics/safe_math.h" | 
|  | 24 #include "base/strings/string16.h" | 
| 24 #include "base/strings/stringprintf.h" | 25 #include "base/strings/stringprintf.h" | 
| 25 #include "base/strings/utf_string_conversions.h" | 26 #include "base/strings/utf_string_conversions.h" | 
| 26 | 27 | 
| 27 namespace crashpad { | 28 namespace crashpad { | 
| 28 | 29 | 
| 29 namespace { | 30 namespace { | 
| 30 | 31 | 
| 31 const wchar_t kDatabaseDirectoryName[] = L"Crashpad"; | 32 const wchar_t kDatabaseDirectoryName[] = L"Crashpad"; | 
| 32 | 33 | 
| 33 const wchar_t kReportsDirectory[] = L"reports"; | 34 const wchar_t kReportsDirectory[] = L"reports"; | 
| 34 const wchar_t kMetadataFileName[] = L"metadata"; | 35 const wchar_t kMetadataFileName[] = L"metadata"; | 
| 35 | 36 | 
| 36 const wchar_t kCrashReportFileExtension[] = L"dmp"; | 37 const wchar_t kCrashReportFileExtension[] = L"dmp"; | 
| 37 | 38 | 
| 38 enum class ReportState : int { | 39 const uint32_t kMetadataFileHeaderMagic = 'CPAD'; | 
|  | 40 const uint32_t kMetadataFileVersion = 1; | 
|  | 41 | 
|  | 42 using OperationStatus = CrashReportDatabase::OperationStatus; | 
|  | 43 | 
|  | 44 // Helpers --------------------------------------------------------------------- | 
|  | 45 | 
|  | 46 // Adds a string to the string table and returns the byte index where it was | 
|  | 47 // added. | 
|  | 48 uint32_t AddStringToTable(std::string* string_table, const std::string& str) { | 
|  | 49   uint32_t offset = base::checked_cast<uint32_t>(string_table->size()); | 
|  | 50   *string_table += str; | 
|  | 51   *string_table += '\0'; | 
|  | 52   return offset; | 
|  | 53 } | 
|  | 54 | 
|  | 55 // Converts |str| to UTF8, adds the result to the string table and returns the | 
|  | 56 // byte index where it was added. | 
|  | 57 uint32_t AddStringToTable(std::string* string_table, | 
|  | 58                           const base::string16& str) { | 
|  | 59   return AddStringToTable(string_table, base::UTF16ToUTF8(str)); | 
|  | 60 } | 
|  | 61 | 
|  | 62 // Reads from the current file position to EOF and returns as a string of bytes. | 
|  | 63 std::string ReadRestOfFileAsString(FileHandle file) { | 
|  | 64   FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR); | 
|  | 65   FileOffset end = LoggingSeekFile(file, 0, SEEK_END); | 
|  | 66   FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET); | 
|  | 67   if (read_from == -1 || end == -1 || original == -1 || read_from == end) | 
|  | 68     return std::string(); | 
|  | 69   DCHECK_EQ(read_from, original); | 
|  | 70   DCHECK_GT(end, read_from); | 
|  | 71   size_t data_length = static_cast<size_t>(end - read_from); | 
|  | 72   std::string buffer(data_length, '\0'); | 
|  | 73   return LoggingReadFile(file, &buffer[0], data_length) ? buffer | 
|  | 74                                                         : std::string(); | 
|  | 75 } | 
|  | 76 | 
|  | 77 // Helper structures, and conversions ------------------------------------------ | 
|  | 78 | 
|  | 79 // The format of the on disk metadata file is a MetadataFileHeader, followed by | 
|  | 80 // a number of fixed size records of MetadataFileReportRecord, followed by a | 
|  | 81 // string table in UTF8 format, where each string is \0 terminated. | 
|  | 82 struct MetadataFileHeader { | 
|  | 83   uint32_t magic; | 
|  | 84   uint32_t version; | 
|  | 85   uint32_t num_records; | 
|  | 86   uint32_t padding; | 
|  | 87 }; | 
|  | 88 | 
|  | 89 struct ReportDisk; | 
|  | 90 | 
|  | 91 enum class ReportState { | 
| 39   //! \brief Created and filled out by caller, owned by database. | 92   //! \brief Created and filled out by caller, owned by database. | 
| 40   kPending, | 93   kPending, | 
| 41   //! \brief In the process of uploading, owned by caller. | 94   //! \brief In the process of uploading, owned by caller. | 
| 42   kUploading, | 95   kUploading, | 
| 43   //! \brief Upload completed or skipped, owned by database. | 96   //! \brief Upload completed or skipped, owned by database. | 
| 44   kCompleted, | 97   kCompleted, | 
| 45 }; | 98 }; | 
| 46 | 99 | 
| 47 using OperationStatus = CrashReportDatabase::OperationStatus; | 100 struct MetadataFileReportRecord { | 
|  | 101   // Note that this default constructor does no initialization. It is used only | 
|  | 102   // to create an array of records that are immediately initialized by reading | 
|  | 103   // from disk in Metadata::Read(). | 
|  | 104   MetadataFileReportRecord() {} | 
| 48 | 105 | 
| 49 //! \brief Ensures that the node at path is a directory, and creates it if it | 106   // Constructs from a ReportDisk, adding to |string_table| and storing indices | 
| 50 //!     does not exist. | 107   // as strings into that table. | 
| 51 //! | 108   MetadataFileReportRecord(const ReportDisk& report, std::string* string_table); | 
| 52 //! \return If the path points to a file, rather than a directory, or the | 109 | 
| 53 //!     directory could not be created, returns `false`. Otherwise, returns | 110   UUID uuid;  // UUID is a 16 byte, standard layout structure. | 
| 54 //!     `true`, indicating that path already was or now is a directory. | 111   uint32_t file_path_index;  // Index into string table. File name is relative | 
| 55 bool CreateOrEnsureDirectoryExists(const base::FilePath& path) { | 112                              // to the reports directory when on disk. | 
| 56   if (CreateDirectory(path.value().c_str(), nullptr)) { | 113   uint32_t id_index;  // Index into string table. | 
| 57     return true; | 114   int64_t creation_time;  // Holds a time_t. | 
| 58   } else if (GetLastError() == ERROR_ALREADY_EXISTS) { | 115   int64_t last_upload_attempt_time;  // Holds a time_t. | 
| 59     DWORD fileattr = GetFileAttributes(path.value().c_str()); | 116   int32_t upload_attempts; | 
| 60     if (fileattr == INVALID_FILE_ATTRIBUTES) { | 117   int32_t state;  // A ReportState. | 
| 61       PLOG(ERROR) << "GetFileAttributes"; | 118   uint8_t uploaded;  // Boolean, 0 or 1. | 
| 62       return false; | 119   uint8_t padding[7]; | 
| 63     } | 120 }; | 
| 64     if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) |  | 
| 65       return true; |  | 
| 66     LOG(ERROR) << "not a directory"; |  | 
| 67     return false; |  | 
| 68   } else { |  | 
| 69     PLOG(ERROR) << "CreateDirectory"; |  | 
| 70     return false; |  | 
| 71   } |  | 
| 72 } |  | 
| 73 | 121 | 
| 74 //! \brief A private extension of the Report class that includes additional data | 122 //! \brief A private extension of the Report class that includes additional data | 
| 75 //!     that's stored on disk in the metadata file. | 123 //!     that's stored on disk in the metadata file. | 
| 76 struct ReportDisk : public CrashReportDatabase::Report { | 124 struct ReportDisk : public CrashReportDatabase::Report { | 
|  | 125   ReportDisk(const MetadataFileReportRecord& record, | 
|  | 126              const base::FilePath& report_dir, | 
|  | 127              const std::string& string_table); | 
|  | 128 | 
|  | 129   ReportDisk(const UUID& uuid, | 
|  | 130              const base::FilePath& path, | 
|  | 131              time_t creation_tim, | 
|  | 132              ReportState state); | 
|  | 133 | 
| 77   //! \brief The current state of the report. | 134   //! \brief The current state of the report. | 
| 78   ReportState state; | 135   ReportState state; | 
| 79 }; | 136 }; | 
| 80 | 137 | 
|  | 138 MetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report, | 
|  | 139                                                    std::string* string_table) | 
|  | 140     : uuid(report.uuid), | 
|  | 141       file_path_index( | 
|  | 142           AddStringToTable(string_table, report.file_path.BaseName().value())), | 
|  | 143       id_index(AddStringToTable(string_table, report.id)), | 
|  | 144       creation_time(report.creation_time), | 
|  | 145       last_upload_attempt_time(report.last_upload_attempt_time), | 
|  | 146       upload_attempts(report.upload_attempts), | 
|  | 147       state(static_cast<uint32_t>(report.state)), | 
|  | 148       uploaded(report.uploaded) { | 
|  | 149   memset(&padding, 0, sizeof(padding)); | 
|  | 150 } | 
|  | 151 | 
|  | 152 ReportDisk::ReportDisk(const MetadataFileReportRecord& record, | 
|  | 153                        const base::FilePath& report_dir, | 
|  | 154                        const std::string& string_table) | 
|  | 155     : Report() { | 
|  | 156   uuid = record.uuid; | 
|  | 157   file_path = report_dir.Append( | 
|  | 158       base::UTF8ToUTF16(&string_table[record.file_path_index])); | 
|  | 159   id = &string_table[record.id_index]; | 
|  | 160   creation_time = record.creation_time; | 
|  | 161   uploaded = record.uploaded; | 
|  | 162   last_upload_attempt_time = record.last_upload_attempt_time; | 
|  | 163   upload_attempts = record.upload_attempts; | 
|  | 164   state = static_cast<ReportState>(record.state); | 
|  | 165 } | 
|  | 166 | 
|  | 167 ReportDisk::ReportDisk(const UUID& uuid, | 
|  | 168                        const base::FilePath& path, | 
|  | 169                        time_t creation_time, | 
|  | 170                        ReportState state) | 
|  | 171     : Report() { | 
|  | 172   this->uuid = uuid; | 
|  | 173   this->file_path = path; | 
|  | 174   this->creation_time = creation_time; | 
|  | 175   this->state = state; | 
|  | 176 } | 
|  | 177 | 
| 81 //! \brief A private extension of the NewReport class to hold the UUID during | 178 //! \brief A private extension of the NewReport class to hold the UUID during | 
| 82 //!     initial write. We don't store metadata in dump's file attributes, and | 179 //!     initial write. We don't store metadata in dump's file attributes, so we | 
| 83 //!     use the UUID to identify the dump on write completion. | 180 //!     use the UUID to identify the dump on write completion. | 
| 84 struct NewReportDisk : public CrashReportDatabase::NewReport { | 181 struct NewReportDisk : public CrashReportDatabase::NewReport { | 
| 85   //! \brief The UUID for this crash report. | 182   //! \brief The UUID for this crash report. | 
| 86   UUID uuid; | 183   UUID uuid; | 
| 87 }; | 184 }; | 
| 88 | 185 | 
|  | 186 // Metadata -------------------------------------------------------------------- | 
|  | 187 | 
| 89 //! \brief Manages the metadata for the set of reports, handling serialization | 188 //! \brief Manages the metadata for the set of reports, handling serialization | 
| 90 //!     to disk, and queries. Instances of this class should be created by using | 189 //!     to disk, and queries. | 
| 91 //!     CrashReportDatabaseWin::AcquireMetadata(). |  | 
| 92 class Metadata { | 190 class Metadata { | 
| 93  public: | 191  public: | 
| 94   //! \brief Writes any changes if necessary, unlocks and closes the file | 192   //! \brief Writes any changes if necessary, unlocks and closes the file | 
| 95   //!     handle. | 193   //!     handle. | 
| 96   ~Metadata(); | 194   ~Metadata(); | 
| 97 | 195 | 
|  | 196   static scoped_ptr<Metadata> Create(const base::FilePath& metadata_file, | 
|  | 197                                      const base::FilePath& report_dir); | 
|  | 198 | 
| 98   //! \brief Adds a new report to the set. | 199   //! \brief Adds a new report to the set. | 
| 99   //! | 200   //! | 
| 100   //! \param[in] new_report_disk The record to add. The #state field must be set | 201   //! \param[in] new_report_disk The record to add. The #state field must be set | 
| 101   //!     to kPending. | 202   //!     to kPending. | 
| 102   void AddNewRecord(const ReportDisk& new_report_disk); | 203   void AddNewRecord(const ReportDisk& new_report_disk); | 
| 103 | 204 | 
| 104   //! \brief Finds all reports in a given state. The \a reports vector is only | 205   //! \brief Finds all reports in a given state. The \a reports vector is only | 
| 105   //!     valid when CrashReportDatabase::kNoError is returned. | 206   //!     valid when CrashReportDatabase::kNoError is returned. | 
| 106   //! | 207   //! | 
| 107   //! \param[in] desired_state The state to match. | 208   //! \param[in] desired_state The state to match. | 
| 108   //! \param[out] reports Matching reports, must be empty on entry. | 209   //! \param[out] reports Matching reports, must be empty on entry. | 
| 109   OperationStatus FindReports( | 210   OperationStatus FindReports( | 
| 110       ReportState desired_state, | 211       ReportState desired_state, | 
| 111       std::vector<const CrashReportDatabase::Report>* reports); | 212       std::vector<const CrashReportDatabase::Report>* reports) const; | 
| 112 | 213 | 
| 113   //! \brief Finds the report matching the given UUID. | 214   //! \brief Finds the report matching the given UUID. | 
| 114   //! | 215   //! | 
| 115   //! The returned report is only valid if CrashReportDatabase::kNoError is | 216   //! The returned report is only valid if CrashReportDatabase::kNoError is | 
| 116   //! returned. | 217   //! returned. | 
| 117   //! | 218   //! | 
| 118   //! \param[in] uuid The report identifier. | 219   //! \param[in] uuid The report identifier. | 
| 119   //! \param[out] report_disk The found report, valid only if | 220   //! \param[out] report_disk The found report, valid only if | 
| 120   //!     CrashReportDatabase::kNoError is returned. Ownership is not | 221   //!     CrashReportDatabase::kNoError is returned. Ownership is not | 
| 121   //!     transferred to the caller, and the report may not be modified. | 222   //!     transferred to the caller, and the report may not be modified. | 
| 122   OperationStatus FindSingleReport(const UUID& uuid, | 223   OperationStatus FindSingleReport(const UUID& uuid, | 
| 123                                    const ReportDisk** report_disk); | 224                                    const ReportDisk** report_disk) const; | 
| 124 | 225 | 
| 125   //! \brief Finds a single report matching the given UUID and in the desired | 226   //! \brief Finds a single report matching the given UUID and in the desired | 
| 126   //!     state and calls the client-supplied mutator to modify the report. | 227   //!     state, and returns a mutable ReportDisk* if found. | 
| 127   //! | 228   //! | 
| 128   //! The mutator object must have an operator()(ReportDisk*) which makes the | 229   //! This marks the metadata as dirty, and on destruction, changes will be | 
| 129   //! desired changes. | 230   //! written to disk via Write(). | 
| 130   //! | 231   //! | 
| 131   //! \return #kNoError on success. #kReportNotFound if there was no report with | 232   //! \return #kNoError on success. #kReportNotFound if there was no report with | 
| 132   //!     the specified UUID. #kBusyError if the report was not in the specified | 233   //!     the specified UUID. #kBusyError if the report was not in the specified | 
| 133   //!     state. | 234   //!     state. | 
| 134   template <class T> | 235   OperationStatus FindSingleReportAndMarkDirty(const UUID& uuid, | 
| 135   OperationStatus MutateSingleReport(const UUID& uuid, | 236                                                ReportState desired_state, | 
| 136                                      ReportState desired_state, | 237                                                ReportDisk** report_disk); | 
| 137                                      const T& mutator); |  | 
| 138 | 238 | 
| 139  private: | 239  private: | 
| 140   static scoped_ptr<Metadata> Create(const base::FilePath& metadata_file, |  | 
| 141                                      const base::FilePath& report_dir); |  | 
| 142   friend class CrashReportDatabaseWin; |  | 
| 143 |  | 
| 144   Metadata(FileHandle handle, const base::FilePath& report_dir); | 240   Metadata(FileHandle handle, const base::FilePath& report_dir); | 
| 145 | 241 | 
| 146   bool Rewind(); | 242   bool Rewind(); | 
| 147 | 243 | 
| 148   void Read(); | 244   void Read(); | 
| 149   void Write(); | 245   void Write(); | 
| 150 | 246 | 
| 151   //! \brief Confirms that the corresponding report actually exists on disk | 247   //! \brief Confirms that the corresponding report actually exists on disk | 
| 152   //!     (that is, the dump file has not been removed), that the report is in | 248   //!     (that is, the dump file has not been removed), and that the report is | 
| 153   //!     the given state. | 249   //!     in the given state. | 
| 154   static OperationStatus VerifyReport(const ReportDisk& report_disk, | 250   static OperationStatus VerifyReport(const ReportDisk& report_disk, | 
| 155                                       ReportState desired_state); | 251                                       ReportState desired_state); | 
| 156   //! \brief Confirms that the corresponding report actually exists on disk | 252   //! \brief Confirms that the corresponding report actually exists on disk | 
| 157   //!     (that is, the dump file has not been removed). | 253   //!     (that is, the dump file has not been removed). | 
| 158   static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk); | 254   static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk); | 
| 159 | 255 | 
| 160   ScopedFileHandle handle_; | 256   ScopedFileHandle handle_; | 
| 161   const base::FilePath report_dir_; | 257   const base::FilePath report_dir_; | 
| 162   bool dirty_;  //! \brief Is a Write() required on destruction? | 258   bool dirty_;  //! \brief `true` when a Write() is required on destruction. | 
| 163   std::vector<ReportDisk> reports_; | 259   std::vector<ReportDisk> reports_; | 
| 164 | 260 | 
| 165   DISALLOW_COPY_AND_ASSIGN(Metadata); | 261   DISALLOW_COPY_AND_ASSIGN(Metadata); | 
| 166 }; | 262 }; | 
| 167 | 263 | 
| 168 Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir) |  | 
| 169     : handle_(handle), report_dir_(report_dir), dirty_(false), reports_() { |  | 
| 170 } |  | 
| 171 |  | 
| 172 Metadata::~Metadata() { | 264 Metadata::~Metadata() { | 
| 173   if (dirty_) | 265   if (dirty_) | 
| 174     Write(); | 266     Write(); | 
| 175   // Not actually async, UnlockFileEx requires the Offset fields. | 267   // Not actually async, UnlockFileEx requires the Offset fields. | 
| 176   OVERLAPPED overlapped = {0}; | 268   OVERLAPPED overlapped = {0}; | 
| 177   if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped)) | 269   if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped)) | 
| 178     PLOG(ERROR) << "UnlockFileEx"; | 270     PLOG(ERROR) << "UnlockFileEx"; | 
| 179 } | 271 } | 
| 180 | 272 | 
| 181 // The format of the metadata file is a MetadataFileHeader, followed by a |  | 
| 182 // number of fixed size records of MetadataFileReportRecord, followed by a |  | 
| 183 // string table in UTF8 format, where each string is \0 terminated. |  | 
| 184 |  | 
| 185 #pragma pack(push, 1) |  | 
| 186 |  | 
| 187 struct MetadataFileHeader { |  | 
| 188   uint32_t magic; |  | 
| 189   uint32_t version; |  | 
| 190   uint32_t num_records; |  | 
| 191   uint32_t padding; |  | 
| 192 }; |  | 
| 193 |  | 
| 194 struct MetadataFileReportRecord { |  | 
| 195   UUID uuid;  // UUID is a 16 byte, standard layout structure. |  | 
| 196   uint32_t file_path_index;  // Index into string table. File name is relative |  | 
| 197                              // to the reports directory when on disk. |  | 
| 198   uint32_t id_index;  // Index into string table. |  | 
| 199   int64_t creation_time;  // Holds a time_t. |  | 
| 200   int64_t last_upload_attempt_time;  // Holds a time_t. |  | 
| 201   int32_t upload_attempts; |  | 
| 202   int32_t state;  // A ReportState. |  | 
| 203   uint8_t uploaded;  // Boolean, 0 or 1. |  | 
| 204   uint8_t padding[7]; |  | 
| 205 }; |  | 
| 206 |  | 
| 207 const uint32_t kMetadataFileHeaderMagic = 'CPAD'; |  | 
| 208 const uint32_t kMetadataFileVersion = 1; |  | 
| 209 |  | 
| 210 #pragma pack(pop) |  | 
| 211 |  | 
| 212 // Reads from the current file position to EOF and returns as uint8_t[]. |  | 
| 213 std::string ReadRestOfFileAsString(FileHandle file) { |  | 
| 214   FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR); |  | 
| 215   FileOffset end = LoggingSeekFile(file, 0, SEEK_END); |  | 
| 216   FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET); |  | 
| 217   if (read_from == -1 || end == -1 || original == -1) |  | 
| 218     return std::string(); |  | 
| 219   DCHECK_EQ(read_from, original); |  | 
| 220   DCHECK_GE(end, read_from); |  | 
| 221   size_t data_length = static_cast<size_t>(end - read_from); |  | 
| 222   std::string buffer(data_length, '\0'); |  | 
| 223   if (!LoggingReadFile(file, &buffer[0], data_length)) |  | 
| 224     return std::string(); |  | 
| 225   return buffer; |  | 
| 226 } |  | 
| 227 |  | 
| 228 uint32_t AddStringToTable(std::string* string_table, const std::string& str) { |  | 
| 229   uint32_t offset = base::checked_cast<uint32_t>(string_table->size()); |  | 
| 230   *string_table += str; |  | 
| 231   *string_table += '\0'; |  | 
| 232   return offset; |  | 
| 233 } |  | 
| 234 |  | 
| 235 uint32_t AddStringToTable(std::string* string_table, const std::wstring& str) { |  | 
| 236   return AddStringToTable(string_table, base::UTF16ToUTF8(str)); |  | 
| 237 } |  | 
| 238 |  | 
| 239 // static | 273 // static | 
| 240 scoped_ptr<Metadata> Metadata::Create(const base::FilePath& metadata_file, | 274 scoped_ptr<Metadata> Metadata::Create(const base::FilePath& metadata_file, | 
| 241                                       const base::FilePath& report_dir) { | 275                                       const base::FilePath& report_dir) { | 
| 242   // It is important that dwShareMode be non-zero so that concurrent access to | 276   // It is important that dwShareMode be non-zero so that concurrent access to | 
| 243   // this file results in a successful open. This allows us to get to LockFileEx | 277   // this file results in a successful open. This allows us to get to LockFileEx | 
| 244   // which then blocks to guard access. | 278   // which then blocks to guard access. | 
| 245   FileHandle handle = CreateFile(metadata_file.value().c_str(), | 279   FileHandle handle = CreateFile(metadata_file.value().c_str(), | 
| 246                                  GENERIC_READ | GENERIC_WRITE, | 280                                  GENERIC_READ | GENERIC_WRITE, | 
| 247                                  FILE_SHARE_READ | FILE_SHARE_WRITE, | 281                                  FILE_SHARE_READ | FILE_SHARE_WRITE, | 
| 248                                  nullptr, | 282                                  nullptr, | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
| 265 | 299 | 
| 266   scoped_ptr<Metadata> metadata(new Metadata(handle, report_dir)); | 300   scoped_ptr<Metadata> metadata(new Metadata(handle, report_dir)); | 
| 267   // If Read() fails, for whatever reason (corruption, etc.) metadata will not | 301   // If Read() fails, for whatever reason (corruption, etc.) metadata will not | 
| 268   // have been modified and will be in a clean empty state. We continue on and | 302   // have been modified and will be in a clean empty state. We continue on and | 
| 269   // return an empty database to hopefully recover. This means that existing | 303   // return an empty database to hopefully recover. This means that existing | 
| 270   // crash reports have been orphaned. | 304   // crash reports have been orphaned. | 
| 271   metadata->Read(); | 305   metadata->Read(); | 
| 272   return metadata; | 306   return metadata; | 
| 273 } | 307 } | 
| 274 | 308 | 
|  | 309 void Metadata::AddNewRecord(const ReportDisk& new_report_disk) { | 
|  | 310   DCHECK(new_report_disk.state == ReportState::kPending); | 
|  | 311   reports_.push_back(new_report_disk); | 
|  | 312   dirty_ = true; | 
|  | 313 } | 
|  | 314 | 
|  | 315 OperationStatus Metadata::FindReports( | 
|  | 316     ReportState desired_state, | 
|  | 317     std::vector<const CrashReportDatabase::Report>* reports) const { | 
|  | 318   DCHECK(reports->empty()); | 
|  | 319   for (const auto& report : reports_) { | 
|  | 320     if (report.state == desired_state && | 
|  | 321         VerifyReport(report, desired_state) == CrashReportDatabase::kNoError) { | 
|  | 322       reports->push_back(report); | 
|  | 323     } | 
|  | 324   } | 
|  | 325   return CrashReportDatabase::kNoError; | 
|  | 326 } | 
|  | 327 | 
|  | 328 OperationStatus Metadata::FindSingleReport( | 
|  | 329     const UUID& uuid, | 
|  | 330     const ReportDisk** out_report) const { | 
|  | 331   auto report_iter = std::find_if( | 
|  | 332       reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { | 
|  | 333         return report.uuid == uuid; | 
|  | 334       }); | 
|  | 335   if (report_iter == reports_.end()) | 
|  | 336     return CrashReportDatabase::kReportNotFound; | 
|  | 337   OperationStatus os = VerifyReportAnyState(*report_iter); | 
|  | 338   if (os == CrashReportDatabase::kNoError) | 
|  | 339     *out_report = &*report_iter; | 
|  | 340   return os; | 
|  | 341 } | 
|  | 342 | 
|  | 343 OperationStatus Metadata::FindSingleReportAndMarkDirty( | 
|  | 344     const UUID& uuid, | 
|  | 345     ReportState desired_state, | 
|  | 346     ReportDisk** report_disk) { | 
|  | 347   auto report_iter = std::find_if( | 
|  | 348       reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { | 
|  | 349         return report.uuid == uuid; | 
|  | 350       }); | 
|  | 351   if (report_iter == reports_.end()) | 
|  | 352     return CrashReportDatabase::kReportNotFound; | 
|  | 353   OperationStatus os = VerifyReport(*report_iter, desired_state); | 
|  | 354   if (os == CrashReportDatabase::kNoError) { | 
|  | 355     dirty_ = true; | 
|  | 356     *report_disk = &*report_iter; | 
|  | 357   } | 
|  | 358   return os; | 
|  | 359 } | 
|  | 360 | 
|  | 361 Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir) | 
|  | 362     : handle_(handle), report_dir_(report_dir), dirty_(false), reports_() { | 
|  | 363 } | 
|  | 364 | 
| 275 bool Metadata::Rewind() { | 365 bool Metadata::Rewind() { | 
| 276   FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET); | 366   FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET); | 
| 277   DCHECK_EQ(result, 0); | 367   DCHECK_EQ(result, 0); | 
| 278   return result == 0; | 368   return result == 0; | 
| 279 } | 369 } | 
| 280 | 370 | 
| 281 void Metadata::Read() { | 371 void Metadata::Read() { | 
| 282   FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END); | 372   FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END); | 
| 283   if (length <= 0)  // Failed, or empty: Abort. | 373   if (length <= 0)  // Failed, or empty: Abort. | 
| 284     return; | 374     return; | 
| 285   if (!Rewind()) { | 375   if (!Rewind()) { | 
| 286     LOG(ERROR) << "failed to rewind to read"; | 376     LOG(ERROR) << "failed to rewind to read"; | 
| 287     return; | 377     return; | 
| 288   } | 378   } | 
| 289 | 379 | 
| 290   MetadataFileHeader header; | 380   MetadataFileHeader header; | 
| 291   if (!LoggingReadFile(handle_.get(), &header, sizeof(header))) { | 381   if (!LoggingReadFile(handle_.get(), &header, sizeof(header))) { | 
| 292     LOG(ERROR) << "failed to read header"; | 382     LOG(ERROR) << "failed to read header"; | 
| 293     return; | 383     return; | 
| 294   } | 384   } | 
| 295   if (header.magic != kMetadataFileHeaderMagic || | 385   if (header.magic != kMetadataFileHeaderMagic || | 
| 296       header.version != kMetadataFileVersion) { | 386       header.version != kMetadataFileVersion) { | 
| 297     LOG(ERROR) << "unexpected header"; | 387     LOG(ERROR) << "unexpected header"; | 
| 298     return; | 388     return; | 
| 299   } | 389   } | 
| 300 | 390 | 
| 301   auto records_size = base::CheckedNumeric<uint32_t>(header.num_records) * | 391   base::CheckedNumeric<uint32_t> records_size = | 
| 302                       sizeof(MetadataFileReportRecord); | 392       base::CheckedNumeric<uint32_t>(header.num_records) * | 
|  | 393       sizeof(MetadataFileReportRecord); | 
| 303   if (!records_size.IsValid()) { | 394   if (!records_size.IsValid()) { | 
| 304     LOG(ERROR) << "record size out of range"; | 395     LOG(ERROR) << "record size out of range"; | 
| 305     return; | 396     return; | 
| 306   } | 397   } | 
| 307 | 398 | 
| 308   scoped_ptr<MetadataFileReportRecord[]> records( | 399   std::vector<MetadataFileReportRecord> records(header.num_records); | 
| 309       new MetadataFileReportRecord[header.num_records]); | 400   if (!LoggingReadFile(handle_.get(), &records[0], records_size.ValueOrDie())) { | 
| 310   if (!LoggingReadFile( |  | 
| 311           handle_.get(), records.get(), records_size.ValueOrDie())) { |  | 
| 312     LOG(ERROR) << "failed to read records"; | 401     LOG(ERROR) << "failed to read records"; | 
| 313     return; | 402     return; | 
| 314   } | 403   } | 
| 315 | 404 | 
| 316   std::string string_table = ReadRestOfFileAsString(handle_.get()); | 405   std::string string_table = ReadRestOfFileAsString(handle_.get()); | 
| 317   if (string_table.empty() || string_table.back() != '\0') { | 406   if (string_table.empty() || string_table.back() != '\0') { | 
| 318     LOG(ERROR) << "bad string table"; | 407     LOG(ERROR) << "bad string table"; | 
| 319     return; | 408     return; | 
| 320   } | 409   } | 
| 321   for (uint32_t i = 0; i < header.num_records; ++i) { | 410 | 
| 322     ReportDisk r; | 411   std::vector<ReportDisk> reports; | 
| 323     const MetadataFileReportRecord* record = &records[i]; | 412   for (const auto& record : records) { | 
| 324     r.uuid = record->uuid; | 413     if (record.file_path_index >= string_table.size() || | 
| 325     if (record->file_path_index >= string_table.size() || | 414         record.id_index >= string_table.size()) { | 
| 326         record->id_index >= string_table.size()) { |  | 
| 327       reports_.clear(); |  | 
| 328       LOG(ERROR) << "invalid string table index"; | 415       LOG(ERROR) << "invalid string table index"; | 
| 329       return; | 416       return; | 
| 330     } | 417     } | 
| 331     r.file_path = report_dir_.Append( | 418     reports.push_back(ReportDisk(record, report_dir_, string_table)); | 
| 332         base::UTF8ToUTF16(&string_table[record->file_path_index])); |  | 
| 333     r.id = &string_table[record->id_index]; |  | 
| 334     r.creation_time = record->creation_time; |  | 
| 335     r.uploaded = record->uploaded; |  | 
| 336     r.last_upload_attempt_time = record->last_upload_attempt_time; |  | 
| 337     r.upload_attempts = record->upload_attempts; |  | 
| 338     r.state = static_cast<ReportState>(record->state); |  | 
| 339     reports_.push_back(r); |  | 
| 340   } | 419   } | 
|  | 420   reports_.swap(reports); | 
| 341 } | 421 } | 
| 342 | 422 | 
| 343 void Metadata::Write() { | 423 void Metadata::Write() { | 
| 344   if (!Rewind()) { | 424   if (!Rewind()) { | 
| 345     LOG(ERROR) << "failed to rewind to write"; | 425     LOG(ERROR) << "failed to rewind to write"; | 
| 346     return; | 426     return; | 
| 347   } | 427   } | 
| 348 | 428 | 
| 349   // Truncate to ensure that a partial write doesn't cause a mix of old and new | 429   // Truncate to ensure that a partial write doesn't cause a mix of old and new | 
| 350   // data causing an incorrect interpretation on read. | 430   // data causing an incorrect interpretation on read. | 
| 351   if (!SetEndOfFile(handle_.get())) { | 431   if (!SetEndOfFile(handle_.get())) { | 
| 352     PLOG(ERROR) << "failed to truncate"; | 432     PLOG(ERROR) << "failed to truncate"; | 
| 353     return; | 433     return; | 
| 354   } | 434   } | 
| 355 | 435 | 
| 356   size_t num_records = reports_.size(); | 436   size_t num_records = reports_.size(); | 
| 357 | 437 | 
| 358   // Fill and write out the header. | 438   // Fill and write out the header. | 
| 359   MetadataFileHeader header = {0}; | 439   MetadataFileHeader header = {0}; | 
| 360   header.magic = kMetadataFileHeaderMagic; | 440   header.magic = kMetadataFileHeaderMagic; | 
| 361   header.version = kMetadataFileVersion; | 441   header.version = kMetadataFileVersion; | 
| 362   header.num_records = base::checked_cast<uint32_t>(num_records); | 442   header.num_records = base::checked_cast<uint32_t>(num_records); | 
| 363   if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) { | 443   if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) { | 
| 364     LOG(ERROR) << "failed to write header"; | 444     LOG(ERROR) << "failed to write header"; | 
| 365     return; | 445     return; | 
| 366   } | 446   } | 
| 367 | 447 | 
| 368   // Build the records and string table we're going to write. | 448   // Build the records and string table we're going to write. | 
| 369   std::string string_table; | 449   std::string string_table; | 
| 370   scoped_ptr<MetadataFileReportRecord[]> records( | 450   std::vector<MetadataFileReportRecord> records; | 
| 371       new MetadataFileReportRecord[num_records]); | 451   records.reserve(num_records); | 
| 372   memset(records.get(), 0, sizeof(MetadataFileReportRecord) * num_records); | 452   for (const auto& report : reports_) { | 
| 373   for (size_t i = 0; i < num_records; ++i) { |  | 
| 374     const ReportDisk& report = reports_[i]; |  | 
| 375     MetadataFileReportRecord& record = records[i]; |  | 
| 376     record.uuid = report.uuid; |  | 
| 377     const base::FilePath& path = report.file_path; | 453     const base::FilePath& path = report.file_path; | 
| 378     if (path.DirName() != report_dir_) { | 454     if (path.DirName() != report_dir_) { | 
| 379       LOG(ERROR) << path.value().c_str() << " expected to start with " | 455       LOG(ERROR) << path.value().c_str() << " expected to start with " | 
| 380                  << report_dir_.value().c_str(); | 456                  << report_dir_.value().c_str(); | 
| 381       return; | 457       return; | 
| 382     } | 458     } | 
| 383     record.file_path_index = | 459     records.push_back(MetadataFileReportRecord(report, &string_table)); | 
| 384         AddStringToTable(&string_table, path.BaseName().value().c_str()); |  | 
| 385     record.id_index = AddStringToTable(&string_table, report.id); |  | 
| 386     record.creation_time = report.creation_time; |  | 
| 387     record.uploaded = report.uploaded; |  | 
| 388     record.last_upload_attempt_time = report.last_upload_attempt_time; |  | 
| 389     record.upload_attempts = report.upload_attempts; |  | 
| 390     record.state = static_cast<uint32_t>(report.state); |  | 
| 391   } | 460   } | 
| 392 | 461 | 
| 393   if (!LoggingWriteFile(handle_.get(), | 462   if (!LoggingWriteFile(handle_.get(), | 
| 394                         records.get(), | 463                         &records[0], | 
| 395                         num_records * sizeof(MetadataFileReportRecord))) { | 464                         records.size() * sizeof(MetadataFileReportRecord))) { | 
| 396     LOG(ERROR) << "failed to write records"; | 465     LOG(ERROR) << "failed to write records"; | 
| 397     return; | 466     return; | 
| 398   } | 467   } | 
| 399   if (!LoggingWriteFile( | 468   if (!LoggingWriteFile( | 
| 400           handle_.get(), string_table.c_str(), string_table.size())) { | 469           handle_.get(), string_table.c_str(), string_table.size())) { | 
| 401     LOG(ERROR) << "failed to write string table"; | 470     LOG(ERROR) << "failed to write string table"; | 
| 402     return; | 471     return; | 
| 403   } | 472   } | 
| 404 } | 473 } | 
| 405 | 474 | 
| 406 void Metadata::AddNewRecord(const ReportDisk& new_report_disk) { |  | 
| 407   DCHECK(new_report_disk.state == ReportState::kPending); |  | 
| 408   reports_.push_back(new_report_disk); |  | 
| 409   dirty_ = true; |  | 
| 410 } |  | 
| 411 |  | 
| 412 OperationStatus Metadata::FindReports( |  | 
| 413     ReportState desired_state, |  | 
| 414     std::vector<const CrashReportDatabase::Report>* reports) { |  | 
| 415   DCHECK(reports->empty()); |  | 
| 416   for (const auto& report : reports_) { |  | 
| 417     if (report.state == desired_state) { |  | 
| 418       if (VerifyReport(report, desired_state) != CrashReportDatabase::kNoError) |  | 
| 419         continue; |  | 
| 420       reports->push_back(report); |  | 
| 421     } |  | 
| 422   } |  | 
| 423   return CrashReportDatabase::kNoError; |  | 
| 424 } |  | 
| 425 |  | 
| 426 OperationStatus Metadata::FindSingleReport(const UUID& uuid, |  | 
| 427                                            const ReportDisk** out_report) { |  | 
| 428   for (size_t i = 0; i < reports_.size(); ++i) { |  | 
| 429     if (reports_[i].uuid == uuid) { |  | 
| 430       OperationStatus os = VerifyReportAnyState(reports_[i]); |  | 
| 431       if (os != CrashReportDatabase::kNoError) |  | 
| 432         return os; |  | 
| 433       *out_report = &reports_[i]; |  | 
| 434       return CrashReportDatabase::kNoError; |  | 
| 435     } |  | 
| 436   } |  | 
| 437   return CrashReportDatabase::kReportNotFound; |  | 
| 438 } |  | 
| 439 |  | 
| 440 template <class T> |  | 
| 441 OperationStatus Metadata::MutateSingleReport( |  | 
| 442     const UUID& uuid, |  | 
| 443     ReportState desired_state, |  | 
| 444     const T& mutator) { |  | 
| 445   for (size_t i = 0; i < reports_.size(); ++i) { |  | 
| 446     if (reports_[i].uuid == uuid) { |  | 
| 447       OperationStatus os = VerifyReport(reports_[i], desired_state); |  | 
| 448       if (os != CrashReportDatabase::kNoError) |  | 
| 449         return os; |  | 
| 450       mutator(&reports_[i]); |  | 
| 451       dirty_ = true; |  | 
| 452       return CrashReportDatabase::kNoError; |  | 
| 453     } |  | 
| 454   } |  | 
| 455   return CrashReportDatabase::kReportNotFound; |  | 
| 456 } |  | 
| 457 |  | 
| 458 // static | 475 // static | 
| 459 OperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) { | 476 OperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) { | 
| 460   DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str()); | 477   DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str()); | 
| 461   if (fileattr == INVALID_FILE_ATTRIBUTES) | 478   if (fileattr == INVALID_FILE_ATTRIBUTES) | 
| 462     return CrashReportDatabase::kReportNotFound; | 479     return CrashReportDatabase::kReportNotFound; | 
| 463   if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) | 480   return (fileattr & FILE_ATTRIBUTE_DIRECTORY) | 
| 464     return CrashReportDatabase::kFileSystemError; | 481              ? CrashReportDatabase::kFileSystemError | 
| 465   return CrashReportDatabase::kNoError; | 482              : CrashReportDatabase::kNoError; | 
| 466 } | 483 } | 
| 467 | 484 | 
| 468 // static | 485 // static | 
| 469 OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk, | 486 OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk, | 
| 470                                        ReportState desired_state) { | 487                                        ReportState desired_state) { | 
| 471   if (report_disk.state != desired_state) | 488   return (report_disk.state == desired_state) | 
| 472     return CrashReportDatabase::kBusyError; | 489              ? VerifyReportAnyState(report_disk) | 
| 473   return VerifyReportAnyState(report_disk); | 490              : CrashReportDatabase::kBusyError; | 
| 474 } | 491 } | 
| 475 | 492 | 
|  | 493 //! \brief Ensures that the node at path is a directory, and creates it if it | 
|  | 494 //!     does not exist. | 
|  | 495 //! | 
|  | 496 //! \return If the path points to a file, rather than a directory, or the | 
|  | 497 //!     directory could not be created, returns `false`. Otherwise, returns | 
|  | 498 //!     `true`, indicating that path already was or now is a directory. | 
|  | 499 bool CreateDirectoryIfNecessary(const base::FilePath& path) { | 
|  | 500   if (CreateDirectory(path.value().c_str(), nullptr)) | 
|  | 501     return true; | 
|  | 502   if (GetLastError() != ERROR_ALREADY_EXISTS) { | 
|  | 503     PLOG(ERROR) << "CreateDirectory"; | 
|  | 504     return false; | 
|  | 505   } | 
|  | 506   DWORD fileattr = GetFileAttributes(path.value().c_str()); | 
|  | 507   if (fileattr == INVALID_FILE_ATTRIBUTES) { | 
|  | 508     PLOG(ERROR) << "GetFileAttributes"; | 
|  | 509     return false; | 
|  | 510   } | 
|  | 511   if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) | 
|  | 512     return true; | 
|  | 513   LOG(ERROR) << "not a directory"; | 
|  | 514   return false; | 
|  | 515 } | 
|  | 516 | 
|  | 517 // CrashReportDatabaseWin ------------------------------------------------------ | 
|  | 518 | 
| 476 class CrashReportDatabaseWin : public CrashReportDatabase { | 519 class CrashReportDatabaseWin : public CrashReportDatabase { | 
| 477  public: | 520  public: | 
| 478   explicit CrashReportDatabaseWin(const base::FilePath& path); | 521   explicit CrashReportDatabaseWin(const base::FilePath& path); | 
| 479   ~CrashReportDatabaseWin() override; | 522   ~CrashReportDatabaseWin() override; | 
| 480 | 523 | 
| 481   bool Initialize(); | 524   bool Initialize(); | 
| 482 | 525 | 
| 483   // CrashReportDatabase: | 526   // CrashReportDatabase: | 
| 484   OperationStatus PrepareNewCrashReport(NewReport** report) override; | 527   OperationStatus PrepareNewCrashReport(NewReport** report) override; | 
| 485   OperationStatus FinishedWritingCrashReport(NewReport* report, | 528   OperationStatus FinishedWritingCrashReport(NewReport* report, | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
| 506 }; | 549 }; | 
| 507 | 550 | 
| 508 CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) | 551 CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) | 
| 509     : CrashReportDatabase(), base_dir_(path) { | 552     : CrashReportDatabase(), base_dir_(path) { | 
| 510 } | 553 } | 
| 511 | 554 | 
| 512 CrashReportDatabaseWin::~CrashReportDatabaseWin() { | 555 CrashReportDatabaseWin::~CrashReportDatabaseWin() { | 
| 513 } | 556 } | 
| 514 | 557 | 
| 515 bool CrashReportDatabaseWin::Initialize() { | 558 bool CrashReportDatabaseWin::Initialize() { | 
| 516   // Check if the database already exists. | 559   // Ensure the database and report subdirectories exist. | 
| 517   if (!CreateOrEnsureDirectoryExists(base_dir_)) | 560   if (!CreateDirectoryIfNecessary(base_dir_) || | 
| 518     return false; | 561       !CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) | 
| 519 |  | 
| 520   // Create our reports subdirectory. |  | 
| 521   if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportsDirectory))) |  | 
| 522     return false; | 562     return false; | 
| 523 | 563 | 
| 524   // TODO(scottmg): When are completed reports pruned from disk? Delete here or | 564   // TODO(scottmg): When are completed reports pruned from disk? Delete here or | 
| 525   // maybe on AcquireMetadata(). | 565   // maybe on AcquireMetadata(). | 
| 526 | 566 | 
| 527   return true; | 567   return true; | 
| 528 } | 568 } | 
| 529 | 569 | 
| 530 OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( | 570 OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( | 
| 531     NewReport** out_report) { | 571     NewReport** report) { | 
| 532   scoped_ptr<NewReportDisk> report(new NewReportDisk()); |  | 
| 533 |  | 
| 534   ::UUID system_uuid; | 572   ::UUID system_uuid; | 
| 535   if (UuidCreate(&system_uuid) != RPC_S_OK) { | 573   if (UuidCreate(&system_uuid) != RPC_S_OK) | 
| 536     return kFileSystemError; | 574     return kFileSystemError; | 
| 537   } |  | 
| 538   static_assert(sizeof(system_uuid) == 16, "unexpected system uuid size"); | 575   static_assert(sizeof(system_uuid) == 16, "unexpected system uuid size"); | 
| 539   static_assert(offsetof(::UUID, Data1) == 0, "unexpected uuid layout"); | 576   static_assert(offsetof(::UUID, Data1) == 0, "unexpected uuid layout"); | 
| 540   UUID uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1)); | 577   UUID uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1)); | 
| 541 | 578 | 
| 542   report->uuid = uuid; | 579   scoped_ptr<NewReportDisk> new_report(new NewReportDisk()); | 
| 543   report->path = | 580   new_report->uuid = uuid; | 
|  | 581   new_report->path = | 
| 544       base_dir_.Append(kReportsDirectory) | 582       base_dir_.Append(kReportsDirectory) | 
| 545           .Append(uuid.ToWideString() + L"." + kCrashReportFileExtension); | 583           .Append(uuid.ToString16() + L"." + kCrashReportFileExtension); | 
| 546   report->handle = LoggingOpenFileForWrite( | 584   new_report->handle = LoggingOpenFileForWrite(new_report->path, | 
| 547       report->path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly); | 585                                                FileWriteMode::kCreateOrFail, | 
| 548   if (report->handle == INVALID_HANDLE_VALUE) | 586                                                FilePermissions::kOwnerOnly); | 
|  | 587   if (new_report->handle == INVALID_HANDLE_VALUE) | 
| 549     return kFileSystemError; | 588     return kFileSystemError; | 
| 550 | 589 | 
| 551   *out_report = report.release(); | 590   *report = new_report.release(); | 
| 552   return kNoError; | 591   return kNoError; | 
| 553 } | 592 } | 
| 554 | 593 | 
| 555 OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( | 594 OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( | 
| 556     NewReport* report, | 595     NewReport* report, | 
| 557     UUID* uuid) { | 596     UUID* uuid) { | 
| 558   // Take ownership of the report, and cast to our private version with UUID. | 597   // Take ownership of the report, and cast to our private version with UUID. | 
| 559   scoped_ptr<NewReportDisk> scoped_report(static_cast<NewReportDisk*>(report)); | 598   scoped_ptr<NewReportDisk> scoped_report(static_cast<NewReportDisk*>(report)); | 
| 560   // Take ownership of the file handle. | 599   // Take ownership of the file handle. | 
| 561   ScopedFileHandle handle(report->handle); | 600   ScopedFileHandle handle(report->handle); | 
| 562 | 601 | 
| 563   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 602   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 564   if (!metadata) | 603   if (!metadata) | 
| 565     return kDatabaseError; | 604     return kDatabaseError; | 
| 566   ReportDisk report_disk; | 605   metadata->AddNewRecord(ReportDisk(scoped_report->uuid, | 
| 567   report_disk.uuid = scoped_report->uuid; | 606                                     scoped_report->path, | 
| 568   report_disk.file_path = scoped_report->path; | 607                                     time(nullptr), | 
| 569   report_disk.creation_time = time(nullptr); | 608                                     ReportState::kPending)); | 
| 570   report_disk.state = ReportState::kPending; | 609   *uuid = scoped_report->uuid; | 
| 571   metadata->AddNewRecord(report_disk); |  | 
| 572   *uuid = report_disk.uuid; |  | 
| 573   return kNoError; | 610   return kNoError; | 
| 574 } | 611 } | 
| 575 | 612 | 
| 576 OperationStatus CrashReportDatabaseWin::ErrorWritingCrashReport( | 613 OperationStatus CrashReportDatabaseWin::ErrorWritingCrashReport( | 
| 577     NewReport* report) { | 614     NewReport* report) { | 
| 578   // Take ownership of the report, and cast to our private version with UUID. | 615   // Take ownership of the report, and cast to our private version with UUID. | 
| 579   scoped_ptr<NewReportDisk> scoped_report(static_cast<NewReportDisk*>(report)); | 616   scoped_ptr<NewReportDisk> scoped_report(static_cast<NewReportDisk*>(report)); | 
| 580 | 617 | 
| 581   // Close the outstanding handle. | 618   // Close the outstanding handle. | 
| 582   LoggingCloseFile(report->handle); | 619   LoggingCloseFile(report->handle); | 
| 583 | 620 | 
| 584   // We failed to write, so remove the dump file. There's no entry in the | 621   // We failed to write, so remove the dump file. There's no entry in the | 
| 585   // metadata table yet. | 622   // metadata table yet. | 
| 586   if (!DeleteFile(scoped_report->path.value().c_str())) { | 623   if (!DeleteFile(scoped_report->path.value().c_str())) { | 
| 587     PLOG(ERROR) << "DeleteFile " << scoped_report->path.value().c_str(); | 624     PLOG(ERROR) << "DeleteFile " << scoped_report->path.value().c_str(); | 
| 588     return CrashReportDatabase::kFileSystemError; | 625     return CrashReportDatabase::kFileSystemError; | 
| 589   } | 626   } | 
| 590 | 627 | 
| 591   return kNoError; | 628   return kNoError; | 
| 592 } | 629 } | 
| 593 | 630 | 
| 594 OperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid, | 631 OperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid, | 
| 595                                                           Report* report) { | 632                                                           Report* report) { | 
| 596   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 633   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 597   if (!metadata) | 634   if (!metadata) | 
| 598     return kDatabaseError; | 635     return kDatabaseError; | 
| 599   // Find and return a copy of the matching report. | 636   // Find and return a copy of the matching report. | 
| 600   const ReportDisk* report_disk; | 637   const ReportDisk* report_disk; | 
| 601   OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); | 638   OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); | 
| 602   if (os != kNoError) | 639   if (os == kNoError) | 
| 603     return os; | 640     *report = *report_disk; | 
| 604   *report = *report_disk; | 641   return os; | 
| 605   return kNoError; |  | 
| 606 } | 642 } | 
| 607 | 643 | 
| 608 OperationStatus CrashReportDatabaseWin::GetPendingReports( | 644 OperationStatus CrashReportDatabaseWin::GetPendingReports( | 
| 609     std::vector<const Report>* reports) { | 645     std::vector<const Report>* reports) { | 
| 610   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 646   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 611   if (!metadata) | 647   return metadata ? metadata->FindReports(ReportState::kPending, reports) | 
| 612     return kDatabaseError; | 648                   : kDatabaseError; | 
| 613   return metadata->FindReports(ReportState::kPending, reports); |  | 
| 614 } | 649 } | 
| 615 | 650 | 
| 616 OperationStatus CrashReportDatabaseWin::GetCompletedReports( | 651 OperationStatus CrashReportDatabaseWin::GetCompletedReports( | 
| 617     std::vector<const Report>* reports) { | 652     std::vector<const Report>* reports) { | 
| 618   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 653   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 619   if (!metadata) | 654   return metadata ? metadata->FindReports(ReportState::kCompleted, reports) | 
| 620     return kDatabaseError; | 655                   : kDatabaseError; | 
| 621   return metadata->FindReports(ReportState::kCompleted, reports); |  | 
| 622 } | 656 } | 
| 623 | 657 | 
| 624 OperationStatus CrashReportDatabaseWin::GetReportForUploading( | 658 OperationStatus CrashReportDatabaseWin::GetReportForUploading( | 
| 625     const UUID& uuid, | 659     const UUID& uuid, | 
| 626     const Report** report) { | 660     const Report** report) { | 
| 627   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 661   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 628   if (!metadata) | 662   if (!metadata) | 
| 629     return kDatabaseError; | 663     return kDatabaseError; | 
| 630   // TODO(scottmg): After returning this report to the client, there is no way | 664   // TODO(scottmg): After returning this report to the client, there is no way | 
| 631   // to reap this report if the uploader fails to call RecordUploadAttempt() or | 665   // to reap this report if the uploader fails to call RecordUploadAttempt() or | 
| 632   // SkipReportUpload() (if it crashed or was otherwise buggy). To resolve this, | 666   // SkipReportUpload() (if it crashed or was otherwise buggy). To resolve this, | 
| 633   // one possibility would be to change the interface to be FileHandle based, so | 667   // one possibility would be to change the interface to be FileHandle based, so | 
| 634   // that instead of giving the file_path back to the client and changing state | 668   // that instead of giving the file_path back to the client and changing state | 
| 635   // to kUploading, we return an exclusive access handle, and use that as the | 669   // to kUploading, we return an exclusive access handle, and use that as the | 
| 636   // signal that the upload is pending, rather than an update to state in the | 670   // signal that the upload is pending, rather than an update to state in the | 
| 637   // metadata. Alternatively, there could be a "garbage collection" at startup | 671   // metadata. Alternatively, there could be a "garbage collection" at startup | 
| 638   // where any reports that are orphaned in the kUploading state are either | 672   // where any reports that are orphaned in the kUploading state are either | 
| 639   // reset to kPending to retry, or discarded. | 673   // reset to kPending to retry, or discarded. | 
| 640   return metadata->MutateSingleReport( | 674   ReportDisk* report_disk; | 
| 641       uuid, ReportState::kPending, [report](ReportDisk* report_disk) { | 675   OperationStatus os = metadata->FindSingleReportAndMarkDirty( | 
| 642         report_disk->state = ReportState::kUploading; | 676       uuid, ReportState::kPending, &report_disk); | 
| 643         // Create a copy for passing back to client. This will be freed in | 677   if (os == CrashReportDatabase::kNoError) { | 
| 644         // RecordUploadAttempt. | 678     report_disk->state = ReportState::kUploading; | 
| 645         *report = new Report(*report_disk); | 679     // Create a copy for passing back to client. This will be freed in | 
| 646       }); | 680     // RecordUploadAttempt. | 
|  | 681     *report = new Report(*report_disk); | 
|  | 682   } | 
|  | 683   return os; | 
| 647 } | 684 } | 
| 648 | 685 | 
| 649 OperationStatus CrashReportDatabaseWin::RecordUploadAttempt( | 686 OperationStatus CrashReportDatabaseWin::RecordUploadAttempt( | 
| 650     const Report* report, | 687     const Report* report, | 
| 651     bool successful, | 688     bool successful, | 
| 652     const std::string& id) { | 689     const std::string& id) { | 
| 653   // Take ownership, allocated in GetReportForUploading. | 690   // Take ownership, allocated in GetReportForUploading. | 
| 654   scoped_ptr<const Report> upload_report(report); | 691   scoped_ptr<const Report> upload_report(report); | 
| 655   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 692   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 656   if (!metadata) | 693   if (!metadata) | 
| 657     return kDatabaseError; | 694     return kDatabaseError; | 
| 658   return metadata->MutateSingleReport( | 695   ReportDisk* report_disk; | 
| 659       report->uuid, | 696   OperationStatus os = metadata->FindSingleReportAndMarkDirty( | 
| 660       ReportState::kUploading, | 697       report->uuid, ReportState::kUploading, &report_disk); | 
| 661       [successful, id](ReportDisk* report_disk) { | 698   if (os == CrashReportDatabaseWin::kNoError) { | 
| 662         report_disk->uploaded = successful; | 699     report_disk->uploaded = successful; | 
| 663         report_disk->id = id; | 700     report_disk->id = id; | 
| 664         report_disk->last_upload_attempt_time = time(nullptr); | 701     report_disk->last_upload_attempt_time = time(nullptr); | 
| 665         report_disk->upload_attempts++; | 702     report_disk->upload_attempts++; | 
| 666         report_disk->state = | 703     report_disk->state = | 
| 667             successful ? ReportState::kCompleted : ReportState::kPending; | 704         successful ? ReportState::kCompleted : ReportState::kPending; | 
| 668       }); | 705   } | 
|  | 706   return os; | 
| 669 } | 707 } | 
| 670 | 708 | 
| 671 OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) { | 709 OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) { | 
| 672   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 710   scoped_ptr<Metadata> metadata(AcquireMetadata()); | 
| 673   if (!metadata) | 711   if (!metadata) | 
| 674     return kDatabaseError; | 712     return kDatabaseError; | 
| 675   return metadata->MutateSingleReport( | 713   ReportDisk* report_disk; | 
| 676       uuid, ReportState::kPending, [](ReportDisk* report_disk) { | 714   OperationStatus os = metadata->FindSingleReportAndMarkDirty( | 
| 677         report_disk->state = ReportState::kCompleted; | 715       uuid, ReportState::kPending, &report_disk); | 
| 678       }); | 716   if (os == CrashReportDatabase::kNoError) | 
|  | 717     report_disk->state = ReportState::kCompleted; | 
|  | 718   return os; | 
| 679 } | 719 } | 
| 680 | 720 | 
| 681 scoped_ptr<Metadata> CrashReportDatabaseWin::AcquireMetadata() { | 721 scoped_ptr<Metadata> CrashReportDatabaseWin::AcquireMetadata() { | 
| 682   base::FilePath metadata_file = base_dir_.Append(kMetadataFileName); | 722   base::FilePath metadata_file = base_dir_.Append(kMetadataFileName); | 
| 683   return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory)); | 723   return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory)); | 
| 684 } | 724 } | 
| 685 | 725 | 
| 686 }  // namespace | 726 }  // namespace | 
| 687 | 727 | 
| 688 // static | 728 // static | 
| 689 scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( | 729 scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( | 
| 690     const base::FilePath& path) { | 730     const base::FilePath& path) { | 
| 691   scoped_ptr<CrashReportDatabaseWin> database_win( | 731   scoped_ptr<CrashReportDatabaseWin> database_win( | 
| 692       new CrashReportDatabaseWin(path.Append(kDatabaseDirectoryName))); | 732       new CrashReportDatabaseWin(path.Append(kDatabaseDirectoryName))); | 
| 693   if (!database_win->Initialize()) | 733   return database_win->Initialize() ? database_win.Pass() | 
| 694     database_win.reset(); | 734                                     : scoped_ptr<CrashReportDatabaseWin>(); | 
| 695 |  | 
| 696   return scoped_ptr<CrashReportDatabase>(database_win.release()); |  | 
| 697 } | 735 } | 
| 698 | 736 | 
| 699 }  // namespace crashpad | 737 }  // namespace crashpad | 
| OLD | NEW | 
|---|