Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (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 | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 #include "client/crash_report_database.h" | |
| 16 | |
| 17 #include <errno.h> | |
| 18 #include <fcntl.h> | |
| 19 #import <Foundation/Foundation.h> | |
| 20 #include <stdio.h> | |
| 21 #include <sys/stat.h> | |
| 22 #include <sys/types.h> | |
| 23 #include <unistd.h> | |
| 24 #include <uuid/uuid.h> | |
| 25 | |
| 26 #include "base/logging.h" | |
| 27 #include "base/posix/eintr_wrapper.h" | |
| 28 #include "base/scoped_generic.h" | |
| 29 #include "base/strings/string_piece.h" | |
| 30 #include "base/strings/stringprintf.h" | |
| 31 #include "base/strings/sys_string_conversions.h" | |
| 32 #include "util/mac/xattr.h" | |
| 33 | |
| 34 namespace crashpad { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 const char kDatabaseDirectoryName[] = "Crashpad"; | |
| 39 | |
| 40 const char kWriteDirectory[] = "new"; | |
| 41 const char kUploadPendingDirectory[] = "completed"; | |
| 42 const char kUploadedDirectory[] = "uploaded"; | |
| 43 | |
| 44 const char* const kReportDirectories[] = { | |
| 45 kWriteDirectory, | |
| 46 kUploadPendingDirectory, | |
| 47 kUploadedDirectory, | |
| 48 }; | |
| 49 | |
| 50 const char kCrashReportFileExtension[] = "dmp"; | |
| 51 | |
| 52 const char kXattrUUID[] = "uuid"; | |
| 53 const char kXattrCollectorID[] = "id"; | |
| 54 const char kXattrCreationTime[] = "creation_time"; | |
| 55 const char kXattrIsUploaded[] = "uploaded"; | |
| 56 const char kXattrLastUploadTime[] = "last_upload_time"; | |
| 57 const char kXattrUploadAttemptCount[] = "upload_count"; | |
| 58 | |
| 59 const char kXattrDatabaseInitialized[] = "initialized"; | |
| 60 | |
| 61 struct ScopedFileLockTraits { | |
| 62 static int InvalidValue() { | |
| 63 return -1; | |
| 64 } | |
| 65 | |
| 66 static void Free(int fd) { | |
| 67 PCHECK(IGNORE_EINTR(close(fd)) == 0) << "close"; | |
| 68 } | |
| 69 }; | |
| 70 | |
| 71 using ScopedFileLock = base::ScopedGeneric<int, ScopedFileLockTraits>; | |
|
Mark Mentovai
2015/01/21 18:01:35
You can just do “using ScopedFileLock = base::Scop
Robert Sesek
2015/01/26 20:16:14
Done.
| |
| 72 | |
| 73 // Ensures that the node at |path| is a directory, and creates it if it does | |
| 74 // not exist. If the |path| points to a file, rather than a directory, or the | |
| 75 // directory could not be created, returns false. Otherwise, returns true, | |
| 76 // indicating that |path| already was or now is a directory. | |
| 77 bool CreateOrEnsureDirectoryExists(const base::FilePath& path) { | |
| 78 if (mkdir(path.value().c_str(), 0755) == 0) { | |
| 79 return true; | |
| 80 } else if (errno == EEXIST) { | |
| 81 struct stat st; | |
| 82 if (stat(path.value().c_str(), &st)) { | |
|
Mark Mentovai
2015/01/21 18:01:35
!= 0 for clarity, otherwise these calls that retur
Robert Sesek
2015/01/26 20:16:14
Done.
| |
| 83 PLOG(ERROR) << "stat"; | |
| 84 return false; | |
| 85 } | |
| 86 if (S_ISDIR(st.st_mode)) { | |
| 87 return true; | |
| 88 } else { | |
| 89 LOG(ERROR) << "not a directory"; | |
| 90 return false; | |
| 91 } | |
| 92 } else { | |
| 93 PLOG(ERROR) << "mkdir"; | |
| 94 return false; | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 //! \brief A CrashReportDatabase that uses HFS+ extended attributes to store | |
| 99 //! report metadata. | |
| 100 //! | |
| 101 //! The database maintains three directories of reports: `"new"` to hold crash | |
| 102 //! reports that are in the process of being written, `"completed"` to hold | |
| 103 //! reports that have been written and are awaing upload, and `"uploaded"` to | |
| 104 //! hold reports successfully uploaded to a collection server. If the user has | |
| 105 //! opted out of report collection, reports will still be written and moved | |
| 106 //! to the completed directory, but they just will not be uploaded. | |
| 107 //! | |
| 108 //! The database stores its metadata in extended filesystem attributes. To | |
| 109 //! ensure safe access, the report file is locked using `O_EXLOCK` during all | |
| 110 //! extended attribute operations. The lock should be obtained using | |
| 111 //! ObtainReportLock(). | |
| 112 class CrashReportDatabaseMac : public CrashReportDatabase { | |
| 113 public: | |
| 114 explicit CrashReportDatabaseMac(const base::FilePath& path); | |
| 115 virtual ~CrashReportDatabaseMac(); | |
| 116 | |
| 117 bool Initialize(); | |
| 118 | |
| 119 // CrashReportDatabase: | |
| 120 OperationStatus PrepareNewCrashReport(FileHandle* handle) override; | |
| 121 OperationStatus FinishedWritingCrashReport(FileHandle handle, | |
| 122 UUID* uuid) override; | |
| 123 OperationStatus LookUpCrashReport(const UUID& uuid, | |
| 124 Report* report) override; | |
| 125 OperationStatus GetNotUploadedReports( | |
| 126 std::vector<const Report>* reports) override; | |
| 127 OperationStatus GetUploadedReports( | |
| 128 std::vector<const Report>* reports) override; | |
| 129 OperationStatus GetReportForUploading(const UUID& uuid, | |
| 130 const Report** report) override; | |
| 131 OperationStatus RecordUploadAttempt(const Report* report, | |
| 132 bool successful, | |
| 133 const std::string& id) override; | |
| 134 | |
| 135 private: | |
| 136 //! \brief A private extension of the Report class that maintains bookkeeping | |
| 137 //! information of the database. | |
| 138 struct UploadReport : public Report { | |
| 139 //! \brief Stores the flock of the file for the duration of | |
| 140 //! GetReportForUploading() and RecordUploadAttempt(). | |
| 141 int lock_fd; | |
| 142 }; | |
| 143 | |
| 144 //! \brief Locates a crash report in the database by UUID. | |
| 145 //! | |
| 146 //! \param[in] uuid The UUID of the crash report to locate. | |
| 147 //! | |
| 148 //! \return The full path to the report file, or an empty path if it cannot be | |
| 149 //! found. | |
| 150 base::FilePath LocateCrashReport(const UUID& uuid); | |
| 151 | |
| 152 //! \brief Obtains an exclusive advisory lock on a file. | |
| 153 //! | |
| 154 //! The flock is used to prevent cross-process concurrent metadata reads or | |
| 155 //! writes. While xattrs do not observe the lock, if the lock-then-mutate | |
| 156 //! protocol is observed by all clients of the database, it still enforces | |
| 157 //! synchronization. | |
| 158 //! | |
| 159 //! This does not block, and so callers must ensure that the lock is valid | |
| 160 //! after calling. | |
| 161 //! | |
| 162 //! \param[in] path The path of the file to lcok. | |
| 163 //! | |
| 164 //! \return A scoped lock object. If the result is not valid, an error is | |
| 165 //! logged. | |
| 166 static ScopedFileLock ObtainReportLock(const base::FilePath& path); | |
| 167 | |
| 168 //! \brief Reads all the database xattrs from a file into a Report. The file | |
| 169 //! must be locked with ObtainReportLock. | |
| 170 //! | |
| 171 //! \param[in] path The path of the report. | |
| 172 //! \param[out] report The object into which data will be read. | |
| 173 //! | |
| 174 //! \return `true` if all the metadata was read successfully, `false` | |
| 175 //! otherwise. | |
| 176 static bool ReadReportMetadataLocked(const base::FilePath& path, | |
| 177 Report* report); | |
| 178 | |
| 179 //! \brief Reads the metadata from all the reports in a database subdirectory. | |
| 180 //! Invalid reports are skipped. | |
| 181 //! | |
| 182 //! \param[in] path The database subdirectory path. | |
| 183 //! \param[out] reports An empty vector of reports, which will be filled. | |
| 184 //! | |
| 185 //! \return The operatino status code. | |
|
Mark Mentovai
2015/01/21 18:01:35
spelling
Robert Sesek
2015/01/26 20:16:14
Done.
| |
| 186 static OperationStatus ReportsInDirectory(const base::FilePath& path, | |
| 187 std::vector<const Report>* reports); | |
| 188 | |
| 189 | |
| 190 //! \brief Creates a database xattr name from the short constant name. | |
| 191 //! | |
| 192 //! \param[in] name The short name of the extended attribute. | |
| 193 //! | |
| 194 //! \return The long name of the extended attribute. | |
| 195 static std::string XattrName(const base::StringPiece& name); | |
| 196 | |
| 197 base::FilePath base_dir_; | |
| 198 | |
| 199 DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac); | |
| 200 }; | |
| 201 | |
| 202 CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) | |
| 203 : CrashReportDatabase(), base_dir_(path) { | |
| 204 } | |
| 205 | |
| 206 CrashReportDatabaseMac::~CrashReportDatabaseMac() {} | |
| 207 | |
| 208 bool CrashReportDatabaseMac::Initialize() { | |
| 209 // Check if the database already exists. | |
| 210 if (!CreateOrEnsureDirectoryExists(base_dir_)) | |
| 211 return false; | |
| 212 | |
| 213 // Create the three processing directories for the database. | |
| 214 for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { | |
| 215 if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i]))) | |
| 216 return false; | |
| 217 } | |
| 218 | |
| 219 // Write an xattr as the last step, to ensure the filesystem has support for | |
| 220 // them. This attribute will never be read. | |
| 221 return WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true); | |
| 222 } | |
| 223 | |
| 224 CrashReportDatabase::OperationStatus | |
| 225 CrashReportDatabaseMac::PrepareNewCrashReport(FileHandle* handle) { | |
| 226 uuid_t uuid_gen; | |
| 227 uuid_generate(uuid_gen); | |
| 228 UUID uuid(uuid_gen); | |
| 229 | |
| 230 base::FilePath path = | |
| 231 base_dir_.Append(kWriteDirectory) | |
| 232 .Append(uuid.ToString() + "." + kCrashReportFileExtension); | |
| 233 | |
| 234 *handle = HANDLE_EINTR(open(path.value().c_str(), | |
| 235 O_CREAT | O_WRONLY | O_EXCL | O_EXLOCK, | |
| 236 0644)); | |
|
Mark Mentovai
2015/01/21 18:01:35
Memory dumps can contain sensitive data, let’s be
Robert Sesek
2015/01/26 20:16:14
Done. That's why I originally had the directory mo
| |
| 237 if (*handle < 0) { | |
| 238 PLOG(ERROR) << "open " << path.value(); | |
| 239 return kFileSystemError; | |
| 240 } | |
| 241 | |
| 242 // TODO(rsesek): Potentially use an fsetxattr() here instead. | |
| 243 if (!WriteXattr(path, XattrName(kXattrUUID), uuid.ToString())) { | |
| 244 PLOG_IF(ERROR, IGNORE_EINTR(close(*handle)) != 0) << "close"; | |
| 245 return kDatabaseError; | |
| 246 } | |
| 247 | |
| 248 return kNoError; | |
| 249 } | |
| 250 | |
| 251 CrashReportDatabase::OperationStatus | |
| 252 CrashReportDatabaseMac::FinishedWritingCrashReport(FileHandle handle, | |
| 253 UUID* uuid) { | |
| 254 // Get the file path for this handle. | |
| 255 char path_buf[MAXPATHLEN] = {0}; | |
| 256 if (fcntl(handle, F_GETPATH, path_buf)) { | |
|
Mark Mentovai
2015/01/21 18:01:35
First: != 0 as above.
More improtantly: system ca
Robert Sesek
2015/01/26 20:16:14
Done.
| |
| 257 PLOG(ERROR) << "fcntl"; | |
| 258 return kFileSystemError; | |
| 259 } | |
| 260 base::FilePath report_path(path_buf); | |
| 261 | |
| 262 ScopedFileLock lock(handle); | |
|
Mark Mentovai
2015/01/21 18:01:35
This should be the first thing in the method, rega
Robert Sesek
2015/01/26 20:16:14
Done.
| |
| 263 | |
| 264 // Get the report's UUID to return. | |
| 265 std::string uuid_string; | |
| 266 if (ReadXattr(report_path, XattrName(kXattrUUID), | |
| 267 &uuid_string) != XattrStatus::kOK || | |
| 268 !uuid->InitializeFromString(uuid_string)) { | |
| 269 LOG(ERROR) << "Failed to read UUID for crash report " | |
| 270 << report_path.value(); | |
| 271 return kDatabaseError; | |
| 272 } | |
| 273 | |
| 274 // Record the creation time of this report. | |
| 275 if (!WriteXattrTimeT(report_path, XattrName(kXattrCreationTime), | |
| 276 time(nullptr))) { | |
| 277 return kDatabaseError; | |
| 278 } | |
| 279 | |
| 280 // Move the report to its new location for uploading. | |
| 281 base::FilePath new_path = | |
| 282 base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName()); | |
| 283 if (rename(report_path.value().c_str(), new_path.value().c_str())) { | |
| 284 PLOG(ERROR) << "rename " << report_path.value() << " to " | |
| 285 << new_path.value(); | |
| 286 return kFileSystemError; | |
| 287 } | |
| 288 | |
| 289 return kNoError; | |
| 290 } | |
| 291 | |
| 292 CrashReportDatabase::OperationStatus | |
| 293 CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid, | |
| 294 CrashReportDatabase::Report* report) { | |
| 295 base::FilePath path = LocateCrashReport(uuid); | |
| 296 if (path.empty()) | |
| 297 return kReportNotFound; | |
| 298 | |
| 299 ScopedFileLock lock(ObtainReportLock(path)); | |
| 300 if (!lock.is_valid()) | |
| 301 return kBusyError; | |
| 302 | |
| 303 *report = Report(); | |
| 304 report->file_path = path; | |
| 305 if (!ReadReportMetadataLocked(path, report)) | |
| 306 return kDatabaseError; | |
| 307 | |
| 308 return kNoError; | |
| 309 } | |
| 310 | |
| 311 CrashReportDatabase::OperationStatus | |
| 312 CrashReportDatabaseMac::GetNotUploadedReports( | |
| 313 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 314 return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports); | |
| 315 } | |
| 316 | |
| 317 CrashReportDatabase::OperationStatus | |
| 318 CrashReportDatabaseMac::GetUploadedReports( | |
| 319 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 320 return ReportsInDirectory(base_dir_.Append(kUploadedDirectory), reports); | |
| 321 } | |
| 322 | |
| 323 CrashReportDatabase::OperationStatus | |
| 324 CrashReportDatabaseMac::GetReportForUploading(const UUID& uuid, | |
| 325 const Report** report) { | |
| 326 base::FilePath report_path = LocateCrashReport(uuid); | |
| 327 if (report_path.empty()) | |
| 328 return kReportNotFound; | |
| 329 | |
| 330 scoped_ptr<UploadReport> upload_report(new UploadReport()); | |
| 331 upload_report->file_path = report_path; | |
| 332 | |
| 333 ScopedFileLock lock(ObtainReportLock(report_path)); | |
| 334 if (!lock.is_valid()) | |
| 335 return kBusyError; | |
| 336 | |
| 337 if (!ReadReportMetadataLocked(report_path, upload_report.get())) | |
| 338 return kDatabaseError; | |
| 339 | |
| 340 upload_report->lock_fd = lock.release(); | |
| 341 *report = upload_report.release(); | |
| 342 return kNoError; | |
| 343 } | |
| 344 | |
| 345 CrashReportDatabase::OperationStatus | |
| 346 CrashReportDatabaseMac::RecordUploadAttempt(const Report* report, | |
| 347 bool successful, | |
| 348 const std::string& id) { | |
| 349 DCHECK(report); | |
| 350 DCHECK(successful || id.empty()); | |
| 351 | |
| 352 base::FilePath report_path = LocateCrashReport(report->uuid); | |
| 353 if (report_path.empty()) | |
| 354 return kReportNotFound; | |
| 355 | |
| 356 scoped_ptr<const UploadReport> upload_report( | |
| 357 static_cast<const UploadReport*>(report)); | |
| 358 | |
| 359 ScopedFileLock lock(upload_report->lock_fd); | |
| 360 if (!lock.is_valid()) | |
| 361 return kBusyError; | |
| 362 | |
| 363 if (successful) { | |
| 364 base::FilePath new_path = | |
| 365 base_dir_.Append(kUploadedDirectory).Append(report_path.BaseName()); | |
| 366 if (rename(report_path.value().c_str(), new_path.value().c_str())) { | |
| 367 PLOG(ERROR) << "rename " << report_path.value() << " to " | |
| 368 << new_path.value(); | |
| 369 return kFileSystemError; | |
| 370 } | |
| 371 report_path = new_path; | |
| 372 } | |
| 373 | |
| 374 if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) { | |
| 375 return kDatabaseError; | |
| 376 } | |
| 377 if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) { | |
| 378 return kDatabaseError; | |
| 379 } | |
| 380 if (!WriteXattrTimeT(report_path, | |
| 381 XattrName(kXattrLastUploadTime), | |
| 382 time(nullptr))) { | |
| 383 return kDatabaseError; | |
| 384 } | |
| 385 | |
| 386 int upload_attempts = 0; | |
| 387 std::string name = XattrName(kXattrUploadAttemptCount); | |
| 388 if (ReadXattrInt(report_path, name, &upload_attempts) == | |
| 389 XattrStatus::kOtherError) { | |
| 390 return kDatabaseError; | |
| 391 } | |
| 392 if (!WriteXattrInt(report_path, name, ++upload_attempts)) { | |
| 393 return kDatabaseError; | |
| 394 } | |
| 395 | |
| 396 return kNoError; | |
| 397 } | |
| 398 | |
| 399 base::FilePath CrashReportDatabaseMac::LocateCrashReport(const UUID& uuid) { | |
| 400 const std::string target_uuid = uuid.ToString(); | |
| 401 for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { | |
| 402 base::FilePath path = | |
| 403 base_dir_.Append(kReportDirectories[i]) | |
| 404 .Append(target_uuid + "." + kCrashReportFileExtension); | |
| 405 | |
| 406 // Test if the path exists. | |
| 407 struct stat st; | |
| 408 if (lstat(path.value().c_str(), &st)) { | |
| 409 continue; | |
| 410 } | |
| 411 | |
| 412 // Check that the UUID of the report matches. | |
| 413 std::string uuid_string; | |
| 414 if (ReadXattr(path, XattrName(kXattrUUID), | |
| 415 &uuid_string) == XattrStatus::kOK && | |
| 416 uuid_string == target_uuid) { | |
| 417 return path; | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 return base::FilePath(); | |
| 422 } | |
| 423 | |
| 424 // static | |
| 425 ScopedFileLock CrashReportDatabaseMac::ObtainReportLock( | |
| 426 const base::FilePath& path) { | |
| 427 int fd = HANDLE_EINTR(open(path.value().c_str(), | |
| 428 O_RDONLY | O_EXLOCK | O_CLOEXEC | O_NONBLOCK)); | |
| 429 PLOG_IF(ERROR, fd < 0) << "open lock " << path.value(); | |
| 430 return ScopedFileLock(fd); | |
| 431 } | |
| 432 | |
| 433 // static | |
| 434 bool CrashReportDatabaseMac::ReadReportMetadataLocked( | |
| 435 const base::FilePath& path, Report* report) { | |
| 436 std::string uuid_string; | |
| 437 if (ReadXattr(path, XattrName(kXattrUUID), | |
| 438 &uuid_string) != XattrStatus::kOK || | |
| 439 !report->uuid.InitializeFromString(uuid_string)) { | |
| 440 return false; | |
| 441 } | |
| 442 | |
| 443 if (ReadXattrTimeT(path, XattrName(kXattrCreationTime), | |
| 444 &report->creation_time) != XattrStatus::kOK) { | |
| 445 return false; | |
| 446 } | |
| 447 | |
| 448 report->id = std::string(); | |
| 449 if (ReadXattr(path, XattrName(kXattrCollectorID), | |
| 450 &report->id) == XattrStatus::kOtherError) { | |
| 451 return false; | |
| 452 } | |
| 453 | |
| 454 report->uploaded = false; | |
| 455 if (ReadXattrBool(path, XattrName(kXattrIsUploaded), | |
| 456 &report->uploaded) == XattrStatus::kOtherError) { | |
| 457 return false; | |
| 458 } | |
| 459 | |
| 460 report->last_upload_attempt_time = 0; | |
| 461 if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime), | |
| 462 &report->last_upload_attempt_time) == | |
| 463 XattrStatus::kOtherError) { | |
| 464 return false; | |
| 465 } | |
| 466 | |
| 467 report->upload_attempts = 0; | |
| 468 if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount), | |
| 469 &report->upload_attempts) == XattrStatus::kOtherError) { | |
| 470 return false; | |
| 471 } | |
| 472 | |
| 473 return true; | |
| 474 } | |
| 475 | |
| 476 // static | |
| 477 CrashReportDatabase::OperationStatus | |
| 478 CrashReportDatabaseMac::ReportsInDirectory( | |
| 479 const base::FilePath& path, | |
| 480 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 481 DCHECK(reports->empty()); | |
| 482 | |
| 483 NSError* error = nil; | |
| 484 NSArray* paths = [[NSFileManager defaultManager] | |
| 485 contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value()) | |
| 486 error:&error]; | |
| 487 if (error) { | |
| 488 LOG(ERROR) << "Failed to enumerate reports in directory " << path.value() | |
| 489 << ": " << [[error description] UTF8String]; | |
| 490 return kFileSystemError; | |
| 491 } | |
| 492 | |
| 493 reports->reserve([paths count]); | |
| 494 for (NSString* entry in paths) { | |
| 495 base::FilePath report_path = path.Append([entry fileSystemRepresentation]); | |
| 496 ScopedFileLock lock(ObtainReportLock(report_path)); | |
| 497 if (!lock.is_valid()) | |
| 498 continue; | |
| 499 | |
| 500 Report report; | |
| 501 if (!ReadReportMetadataLocked(report_path, &report)) { | |
| 502 LOG(WARNING) << "Failed to read report metadata for " | |
| 503 << report_path.value(); | |
| 504 continue; | |
| 505 } | |
| 506 reports->push_back(report); | |
| 507 } | |
| 508 | |
| 509 return kNoError; | |
| 510 } | |
| 511 | |
| 512 // static | |
| 513 std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) { | |
| 514 return base::StringPrintf("com.googlecode.crashpad.%s", name.data()); | |
| 515 } | |
| 516 | |
| 517 } // namespace | |
| 518 | |
| 519 // static | |
| 520 scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( | |
| 521 const base::FilePath& path) { | |
| 522 scoped_ptr<CrashReportDatabaseMac> database_mac( | |
| 523 new CrashReportDatabaseMac(path.Append(kDatabaseDirectoryName))); | |
| 524 if (!database_mac->Initialize()) | |
| 525 database_mac.reset(); | |
| 526 | |
| 527 return scoped_ptr<CrashReportDatabase>(database_mac.release()); | |
| 528 } | |
| 529 | |
| 530 } // namespace crashpad | |
| OLD | NEW |