2015-01-26 15:59:01 -05:00
|
|
|
// Copyright 2015 The Crashpad Authors. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include "client/crash_report_database.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#import <Foundation/Foundation.h>
|
2017-04-13 10:12:28 -04:00
|
|
|
#include <stdint.h>
|
2015-01-26 15:59:01 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2015-03-10 12:22:31 -04:00
|
|
|
#include <time.h>
|
2015-01-26 15:59:01 -05:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <uuid/uuid.h>
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
2015-03-09 18:47:52 -04:00
|
|
|
#include "base/mac/scoped_nsautorelease_pool.h"
|
2015-01-26 15:59:01 -05:00
|
|
|
#include "base/posix/eintr_wrapper.h"
|
|
|
|
#include "base/scoped_generic.h"
|
|
|
|
#include "base/strings/string_piece.h"
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
#include "base/strings/sys_string_conversions.h"
|
2015-03-09 18:47:52 -04:00
|
|
|
#include "client/settings.h"
|
2015-01-26 15:59:01 -05:00
|
|
|
#include "util/file/file_io.h"
|
|
|
|
#include "util/mac/xattr.h"
|
2015-03-10 13:24:44 -04:00
|
|
|
#include "util/misc/initialization_state_dcheck.h"
|
2016-09-02 12:10:57 -07:00
|
|
|
#include "util/misc/metrics.h"
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr char kWriteDirectory[] = "new";
|
|
|
|
constexpr char kUploadPendingDirectory[] = "pending";
|
|
|
|
constexpr char kCompletedDirectory[] = "completed";
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr char kSettings[] = "settings.dat";
|
2015-03-09 18:47:52 -04:00
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr const char* kReportDirectories[] = {
|
2016-08-24 15:37:46 -04:00
|
|
|
kWriteDirectory,
|
|
|
|
kUploadPendingDirectory,
|
|
|
|
kCompletedDirectory,
|
2015-01-26 15:59:01 -05:00
|
|
|
};
|
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr char kCrashReportFileExtension[] = "dmp";
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr char kXattrUUID[] = "uuid";
|
|
|
|
constexpr char kXattrCollectorID[] = "id";
|
|
|
|
constexpr char kXattrCreationTime[] = "creation_time";
|
|
|
|
constexpr char kXattrIsUploaded[] = "uploaded";
|
|
|
|
constexpr char kXattrLastUploadTime[] = "last_upload_time";
|
|
|
|
constexpr char kXattrUploadAttemptCount[] = "upload_count";
|
|
|
|
constexpr char kXattrIsUploadExplicitlyRequested[] =
|
|
|
|
"upload_explicitly_requested";
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2017-07-25 13:34:04 -04:00
|
|
|
constexpr char kXattrDatabaseInitialized[] = "initialized";
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2015-10-08 13:10:02 -04:00
|
|
|
// Ensures that the node at |path| is a directory. If the |path| refers to a
|
|
|
|
// file, rather than a directory, returns false. Otherwise, returns true,
|
|
|
|
// indicating that |path| already was a directory.
|
|
|
|
bool EnsureDirectoryExists(const base::FilePath& path) {
|
|
|
|
struct stat st;
|
|
|
|
if (stat(path.value().c_str(), &st) != 0) {
|
|
|
|
PLOG(ERROR) << "stat " << path.value();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
|
|
LOG(ERROR) << "stat " << path.value() << ": not a directory";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
// Ensures that the node at |path| is a directory, and creates it if it does
|
2015-10-08 13:10:02 -04:00
|
|
|
// not exist. If the |path| refers to a file, rather than a directory, or the
|
2015-01-26 15:59:01 -05:00
|
|
|
// directory could not be created, returns false. Otherwise, returns true,
|
|
|
|
// indicating that |path| already was or now is a directory.
|
|
|
|
bool CreateOrEnsureDirectoryExists(const base::FilePath& path) {
|
|
|
|
if (mkdir(path.value().c_str(), 0755) == 0) {
|
|
|
|
return true;
|
2015-10-08 13:10:02 -04:00
|
|
|
}
|
|
|
|
if (errno != EEXIST) {
|
|
|
|
PLOG(ERROR) << "mkdir " << path.value();
|
2015-01-26 15:59:01 -05:00
|
|
|
return false;
|
|
|
|
}
|
2015-10-08 13:10:02 -04:00
|
|
|
return EnsureDirectoryExists(path);
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
2015-10-29 18:31:20 -04:00
|
|
|
// Creates a long database xattr name from the short constant name. These names
|
|
|
|
// have changed, and new_name determines whether the returned xattr name will be
|
|
|
|
// the old name or its new equivalent.
|
|
|
|
std::string XattrNameInternal(const base::StringPiece& name, bool new_name) {
|
|
|
|
return base::StringPrintf(new_name ? "org.chromium.crashpad.database.%s"
|
|
|
|
: "com.googlecode.crashpad.%s",
|
|
|
|
name.data());
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
//! \brief A CrashReportDatabase that uses HFS+ extended attributes to store
|
|
|
|
//! report metadata.
|
|
|
|
//!
|
|
|
|
//! The database maintains three directories of reports: `"new"` to hold crash
|
|
|
|
//! reports that are in the process of being written, `"completed"` to hold
|
2015-02-11 12:17:05 -08:00
|
|
|
//! reports that have been written and are awaiting upload, and `"uploaded"` to
|
2015-01-26 15:59:01 -05:00
|
|
|
//! hold reports successfully uploaded to a collection server. If the user has
|
|
|
|
//! opted out of report collection, reports will still be written and moved
|
|
|
|
//! to the completed directory, but they just will not be uploaded.
|
|
|
|
//!
|
|
|
|
//! The database stores its metadata in extended filesystem attributes. To
|
|
|
|
//! ensure safe access, the report file is locked using `O_EXLOCK` during all
|
|
|
|
//! extended attribute operations. The lock should be obtained using
|
|
|
|
//! ObtainReportLock().
|
|
|
|
class CrashReportDatabaseMac : public CrashReportDatabase {
|
|
|
|
public:
|
|
|
|
explicit CrashReportDatabaseMac(const base::FilePath& path);
|
|
|
|
virtual ~CrashReportDatabaseMac();
|
|
|
|
|
2015-10-08 13:10:02 -04:00
|
|
|
bool Initialize(bool may_create);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
// CrashReportDatabase:
|
2015-03-09 18:47:52 -04:00
|
|
|
Settings* GetSettings() override;
|
2015-01-26 15:59:01 -05:00
|
|
|
OperationStatus PrepareNewCrashReport(NewReport** report) override;
|
|
|
|
OperationStatus FinishedWritingCrashReport(NewReport* report,
|
|
|
|
UUID* uuid) override;
|
2015-02-04 16:33:15 -05:00
|
|
|
OperationStatus ErrorWritingCrashReport(NewReport* report) override;
|
2015-10-08 13:10:02 -04:00
|
|
|
OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override;
|
2015-03-06 18:43:28 -05:00
|
|
|
OperationStatus GetPendingReports(std::vector<Report>* reports) override;
|
|
|
|
OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
|
2015-01-26 15:59:01 -05:00
|
|
|
OperationStatus GetReportForUploading(const UUID& uuid,
|
|
|
|
const Report** report) override;
|
|
|
|
OperationStatus RecordUploadAttempt(const Report* report,
|
|
|
|
bool successful,
|
|
|
|
const std::string& id) override;
|
2016-09-26 15:06:19 -07:00
|
|
|
OperationStatus SkipReportUpload(const UUID& uuid,
|
|
|
|
Metrics::CrashSkippedReason reason) override;
|
2015-10-07 17:01:47 -04:00
|
|
|
OperationStatus DeleteReport(const UUID& uuid) override;
|
2016-08-24 15:37:46 -04:00
|
|
|
OperationStatus RequestUpload(const UUID& uuid) override;
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
private:
|
2017-04-13 10:12:28 -04:00
|
|
|
//! \brief Report states for use with LocateCrashReport().
|
|
|
|
//!
|
|
|
|
//! ReportState may be considered to be a bitfield.
|
|
|
|
enum ReportState : uint8_t {
|
|
|
|
kReportStateWrite = 1 << 0, // in kWriteDirectory
|
|
|
|
kReportStatePending = 1 << 1, // in kUploadPendingDirectory
|
|
|
|
kReportStateCompleted = 1 << 2, // in kCompletedDirectory
|
|
|
|
kReportStateAny =
|
|
|
|
kReportStateWrite | kReportStatePending | kReportStateCompleted,
|
|
|
|
};
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
//! \brief A private extension of the Report class that maintains bookkeeping
|
|
|
|
//! information of the database.
|
|
|
|
struct UploadReport : public Report {
|
|
|
|
//! \brief Stores the flock of the file for the duration of
|
|
|
|
//! GetReportForUploading() and RecordUploadAttempt().
|
|
|
|
int lock_fd;
|
|
|
|
};
|
|
|
|
|
|
|
|
//! \brief Locates a crash report in the database by UUID.
|
|
|
|
//!
|
|
|
|
//! \param[in] uuid The UUID of the crash report to locate.
|
2017-04-13 10:12:28 -04:00
|
|
|
//! \param[in] desired_state The state of the report to locate, composed of
|
|
|
|
//! ReportState values.
|
2015-01-26 15:59:01 -05:00
|
|
|
//!
|
|
|
|
//! \return The full path to the report file, or an empty path if it cannot be
|
|
|
|
//! found.
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath LocateCrashReport(const UUID& uuid, uint8_t desired_state);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
//! \brief Obtains an exclusive advisory lock on a file.
|
|
|
|
//!
|
|
|
|
//! The flock is used to prevent cross-process concurrent metadata reads or
|
|
|
|
//! writes. While xattrs do not observe the lock, if the lock-then-mutate
|
|
|
|
//! protocol is observed by all clients of the database, it still enforces
|
|
|
|
//! synchronization.
|
|
|
|
//!
|
|
|
|
//! This does not block, and so callers must ensure that the lock is valid
|
|
|
|
//! after calling.
|
|
|
|
//!
|
2015-02-11 12:17:05 -08:00
|
|
|
//! \param[in] path The path of the file to lock.
|
2015-01-26 15:59:01 -05:00
|
|
|
//!
|
|
|
|
//! \return A scoped lock object. If the result is not valid, an error is
|
|
|
|
//! logged.
|
|
|
|
static base::ScopedFD ObtainReportLock(const base::FilePath& path);
|
|
|
|
|
|
|
|
//! \brief Reads all the database xattrs from a file into a Report. The file
|
|
|
|
//! must be locked with ObtainReportLock.
|
|
|
|
//!
|
|
|
|
//! \param[in] path The path of the report.
|
|
|
|
//! \param[out] report The object into which data will be read.
|
|
|
|
//!
|
|
|
|
//! \return `true` if all the metadata was read successfully, `false`
|
|
|
|
//! otherwise.
|
2015-10-29 18:31:20 -04:00
|
|
|
bool ReadReportMetadataLocked(const base::FilePath& path, Report* report);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
//! \brief Reads the metadata from all the reports in a database subdirectory.
|
|
|
|
//! Invalid reports are skipped.
|
|
|
|
//!
|
|
|
|
//! \param[in] path The database subdirectory path.
|
|
|
|
//! \param[out] reports An empty vector of reports, which will be filled.
|
|
|
|
//!
|
|
|
|
//! \return The operation status code.
|
2015-10-29 18:31:20 -04:00
|
|
|
OperationStatus ReportsInDirectory(const base::FilePath& path,
|
|
|
|
std::vector<Report>* reports);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
//! \brief Creates a database xattr name from the short constant name.
|
|
|
|
//!
|
|
|
|
//! \param[in] name The short name of the extended attribute.
|
|
|
|
//!
|
|
|
|
//! \return The long name of the extended attribute.
|
2015-10-29 18:31:20 -04:00
|
|
|
std::string XattrName(const base::StringPiece& name);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
//! \brief Marks a report with a given path as completed.
|
|
|
|
//!
|
|
|
|
//! Assumes that the report is locked.
|
|
|
|
//!
|
|
|
|
//! \param[in] report_path The path of the file to mark completed.
|
|
|
|
//! \param[out] out_path The path of the new file. This parameter is optional.
|
|
|
|
//!
|
|
|
|
//! \return The operation status code.
|
|
|
|
CrashReportDatabase::OperationStatus MarkReportCompletedLocked(
|
|
|
|
const base::FilePath& report_path,
|
|
|
|
base::FilePath* out_path);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
base::FilePath base_dir_;
|
2015-03-09 18:47:52 -04:00
|
|
|
Settings settings_;
|
2015-10-29 18:31:20 -04:00
|
|
|
bool xattr_new_names_;
|
2015-03-10 13:24:44 -04:00
|
|
|
InitializationStateDcheck initialized_;
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac);
|
|
|
|
};
|
|
|
|
|
|
|
|
CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path)
|
2015-03-09 18:47:52 -04:00
|
|
|
: CrashReportDatabase(),
|
|
|
|
base_dir_(path),
|
2015-03-10 13:24:44 -04:00
|
|
|
settings_(base_dir_.Append(kSettings)),
|
2015-10-29 18:31:20 -04:00
|
|
|
xattr_new_names_(false),
|
2015-03-10 13:24:44 -04:00
|
|
|
initialized_() {
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabaseMac::~CrashReportDatabaseMac() {}
|
|
|
|
|
2015-10-08 13:10:02 -04:00
|
|
|
bool CrashReportDatabaseMac::Initialize(bool may_create) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
// Check if the database already exists.
|
2015-10-08 13:10:02 -04:00
|
|
|
if (may_create) {
|
|
|
|
if (!CreateOrEnsureDirectoryExists(base_dir_)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (!EnsureDirectoryExists(base_dir_)) {
|
2015-01-26 15:59:01 -05:00
|
|
|
return false;
|
2015-10-08 13:10:02 -04:00
|
|
|
}
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
// Create the three processing directories for the database.
|
|
|
|
for (size_t i = 0; i < arraysize(kReportDirectories); ++i) {
|
|
|
|
if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-09 18:47:52 -04:00
|
|
|
if (!settings_.Initialize())
|
|
|
|
return false;
|
|
|
|
|
2015-10-29 18:31:20 -04:00
|
|
|
// Do an xattr operation as the last step, to ensure the filesystem has
|
|
|
|
// support for them. This xattr also serves as a marker for whether the
|
|
|
|
// database uses old or new xattr names.
|
|
|
|
bool value;
|
|
|
|
if (ReadXattrBool(base_dir_,
|
|
|
|
XattrNameInternal(kXattrDatabaseInitialized, true),
|
|
|
|
&value) == XattrStatus::kOK &&
|
|
|
|
value) {
|
|
|
|
xattr_new_names_ = true;
|
|
|
|
} else if (ReadXattrBool(base_dir_,
|
|
|
|
XattrNameInternal(kXattrDatabaseInitialized, false),
|
|
|
|
&value) == XattrStatus::kOK &&
|
|
|
|
value) {
|
|
|
|
xattr_new_names_ = false;
|
|
|
|
} else {
|
|
|
|
xattr_new_names_ = true;
|
|
|
|
if (!WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true))
|
|
|
|
return false;
|
|
|
|
}
|
2015-03-10 13:24:44 -04:00
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
return true;
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
2015-03-09 18:47:52 -04:00
|
|
|
Settings* CrashReportDatabaseMac::GetSettings() {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
2015-03-09 18:47:52 -04:00
|
|
|
return &settings_;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<NewReport> report(new NewReport());
|
2015-03-13 13:00:56 -04:00
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
uuid_t uuid_gen;
|
|
|
|
uuid_generate(uuid_gen);
|
2015-03-13 13:00:56 -04:00
|
|
|
report->uuid.InitializeFromBytes(uuid_gen);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
report->path =
|
|
|
|
base_dir_.Append(kWriteDirectory)
|
2015-03-13 13:00:56 -04:00
|
|
|
.Append(report->uuid.ToString() + "." + kCrashReportFileExtension);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2017-02-28 16:15:25 -05:00
|
|
|
report->handle = HANDLE_EINTR(
|
|
|
|
open(report->path.value().c_str(),
|
|
|
|
O_WRONLY | O_EXLOCK | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,
|
|
|
|
0600));
|
2015-01-26 15:59:01 -05:00
|
|
|
if (report->handle < 0) {
|
|
|
|
PLOG(ERROR) << "open " << report->path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(rsesek): Potentially use an fsetxattr() here instead.
|
2015-03-13 13:00:56 -04:00
|
|
|
if (!WriteXattr(
|
|
|
|
report->path, XattrName(kXattrUUID), report->uuid.ToString())) {
|
2015-01-26 15:59:01 -05:00
|
|
|
PLOG_IF(ERROR, IGNORE_EINTR(close(report->handle)) != 0) << "close";
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_report = report.release();
|
|
|
|
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::FinishedWritingCrashReport(NewReport* report,
|
|
|
|
UUID* uuid) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
// Takes ownership of the |handle| and the O_EXLOCK.
|
|
|
|
base::ScopedFD lock(report->handle);
|
|
|
|
|
|
|
|
// Take ownership of the report.
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<NewReport> scoped_report(report);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
// Get the report's UUID to return.
|
|
|
|
std::string uuid_string;
|
|
|
|
if (ReadXattr(report->path, XattrName(kXattrUUID),
|
|
|
|
&uuid_string) != XattrStatus::kOK ||
|
|
|
|
!uuid->InitializeFromString(uuid_string)) {
|
|
|
|
LOG(ERROR) << "Failed to read UUID for crash report "
|
|
|
|
<< report->path.value();
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
2015-03-13 13:00:56 -04:00
|
|
|
if (*uuid != report->uuid) {
|
|
|
|
LOG(ERROR) << "UUID mismatch for crash report " << report->path.value();
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
// Record the creation time of this report.
|
|
|
|
if (!WriteXattrTimeT(report->path, XattrName(kXattrCreationTime),
|
|
|
|
time(nullptr))) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move the report to its new location for uploading.
|
|
|
|
base::FilePath new_path =
|
|
|
|
base_dir_.Append(kUploadPendingDirectory).Append(report->path.BaseName());
|
|
|
|
if (rename(report->path.value().c_str(), new_path.value().c_str()) != 0) {
|
|
|
|
PLOG(ERROR) << "rename " << report->path.value() << " to "
|
|
|
|
<< new_path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
2016-09-26 15:06:19 -07:00
|
|
|
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
|
2016-09-02 12:10:57 -07:00
|
|
|
Metrics::CrashReportSize(report->handle);
|
2016-09-01 17:01:31 -07:00
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
2015-02-04 16:33:15 -05:00
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::ErrorWritingCrashReport(NewReport* report) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2015-02-04 16:33:15 -05:00
|
|
|
// Takes ownership of the |handle| and the O_EXLOCK.
|
|
|
|
base::ScopedFD lock(report->handle);
|
|
|
|
|
|
|
|
// Take ownership of the report.
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<NewReport> scoped_report(report);
|
2015-02-04 16:33:15 -05:00
|
|
|
|
|
|
|
// Remove the file that the report would have been written to had no error
|
|
|
|
// occurred.
|
|
|
|
if (unlink(report->path.value().c_str()) != 0) {
|
|
|
|
PLOG(ERROR) << "unlink " << report->path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid,
|
|
|
|
CrashReportDatabase::Report* report) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath path = LocateCrashReport(uuid, kReportStateAny);
|
2015-01-26 15:59:01 -05:00
|
|
|
if (path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
|
|
|
base::ScopedFD lock(ObtainReportLock(path));
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
|
|
|
*report = Report();
|
|
|
|
report->file_path = path;
|
|
|
|
if (!ReadReportMetadataLocked(path, report))
|
|
|
|
return kDatabaseError;
|
|
|
|
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::GetPendingReports(
|
2015-03-06 18:43:28 -05:00
|
|
|
std::vector<CrashReportDatabase::Report>* reports) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports);
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::GetCompletedReports(
|
2015-03-06 18:43:28 -05:00
|
|
|
std::vector<CrashReportDatabase::Report>* reports) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
return ReportsInDirectory(base_dir_.Append(kCompletedDirectory), reports);
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::GetReportForUploading(const UUID& uuid,
|
|
|
|
const Report** report) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath report_path = LocateCrashReport(uuid, kReportStatePending);
|
2015-01-26 15:59:01 -05:00
|
|
|
if (report_path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<UploadReport> upload_report(new UploadReport());
|
2015-01-26 15:59:01 -05:00
|
|
|
upload_report->file_path = report_path;
|
|
|
|
|
|
|
|
base::ScopedFD lock(ObtainReportLock(report_path));
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
|
|
|
if (!ReadReportMetadataLocked(report_path, upload_report.get()))
|
|
|
|
return kDatabaseError;
|
|
|
|
|
|
|
|
upload_report->lock_fd = lock.release();
|
|
|
|
*report = upload_report.release();
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::RecordUploadAttempt(const Report* report,
|
|
|
|
bool successful,
|
|
|
|
const std::string& id) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2016-09-26 15:06:19 -07:00
|
|
|
Metrics::CrashUploadAttempted(successful);
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
DCHECK(report);
|
|
|
|
DCHECK(successful || id.empty());
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath report_path =
|
|
|
|
LocateCrashReport(report->uuid, kReportStatePending);
|
2015-01-26 15:59:01 -05:00
|
|
|
if (report_path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<const UploadReport> upload_report(
|
2015-01-26 15:59:01 -05:00
|
|
|
static_cast<const UploadReport*>(report));
|
|
|
|
|
|
|
|
base::ScopedFD lock(upload_report->lock_fd);
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
|
|
|
if (successful) {
|
2016-08-24 15:37:46 -04:00
|
|
|
CrashReportDatabase::OperationStatus os =
|
|
|
|
MarkReportCompletedLocked(report_path, &report_path);
|
|
|
|
if (os != kNoError)
|
|
|
|
return os;
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
2015-03-10 12:22:31 -04:00
|
|
|
|
|
|
|
time_t now = time(nullptr);
|
|
|
|
if (!WriteXattrTimeT(report_path, XattrName(kXattrLastUploadTime), now)) {
|
2015-01-26 15:59:01 -05:00
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
int upload_attempts = 0;
|
|
|
|
std::string name = XattrName(kXattrUploadAttemptCount);
|
|
|
|
if (ReadXattrInt(report_path, name, &upload_attempts) ==
|
|
|
|
XattrStatus::kOtherError) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
if (!WriteXattrInt(report_path, name, ++upload_attempts)) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
2015-03-10 12:22:31 -04:00
|
|
|
if (!settings_.SetLastUploadAttemptTime(now)) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload(
|
2016-09-26 15:06:19 -07:00
|
|
|
const UUID& uuid,
|
|
|
|
Metrics::CrashSkippedReason reason) {
|
2015-03-10 13:24:44 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2016-09-26 15:06:19 -07:00
|
|
|
Metrics::CrashUploadSkipped(reason);
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath report_path = LocateCrashReport(uuid, kReportStatePending);
|
2015-01-26 15:59:01 -05:00
|
|
|
if (report_path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
|
|
|
base::ScopedFD lock(ObtainReportLock(report_path));
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
return MarkReportCompletedLocked(report_path, nullptr);
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
2015-10-07 17:01:47 -04:00
|
|
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport(
|
|
|
|
const UUID& uuid) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath report_path = LocateCrashReport(uuid, kReportStateAny);
|
2015-10-07 17:01:47 -04:00
|
|
|
if (report_path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
|
|
|
base::ScopedFD lock(ObtainReportLock(report_path));
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
|
|
|
if (unlink(report_path.value().c_str()) != 0) {
|
|
|
|
PLOG(ERROR) << "unlink " << report_path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath CrashReportDatabaseMac::LocateCrashReport(
|
|
|
|
const UUID& uuid,
|
|
|
|
uint8_t desired_state) {
|
2015-01-26 15:59:01 -05:00
|
|
|
const std::string target_uuid = uuid.ToString();
|
2017-04-13 10:12:28 -04:00
|
|
|
|
|
|
|
std::vector<std::string> report_directories;
|
|
|
|
if (desired_state & kReportStateWrite) {
|
|
|
|
report_directories.push_back(kWriteDirectory);
|
|
|
|
}
|
|
|
|
if (desired_state & kReportStatePending) {
|
|
|
|
report_directories.push_back(kUploadPendingDirectory);
|
|
|
|
}
|
|
|
|
if (desired_state & kReportStateCompleted) {
|
|
|
|
report_directories.push_back(kCompletedDirectory);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const std::string& report_directory : report_directories) {
|
2015-01-26 15:59:01 -05:00
|
|
|
base::FilePath path =
|
2017-04-13 10:12:28 -04:00
|
|
|
base_dir_.Append(report_directory)
|
2015-10-08 13:10:02 -04:00
|
|
|
.Append(target_uuid + "." + kCrashReportFileExtension);
|
2015-01-26 15:59:01 -05:00
|
|
|
|
|
|
|
// Test if the path exists.
|
|
|
|
struct stat st;
|
|
|
|
if (lstat(path.value().c_str(), &st)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the UUID of the report matches.
|
|
|
|
std::string uuid_string;
|
|
|
|
if (ReadXattr(path, XattrName(kXattrUUID),
|
|
|
|
&uuid_string) == XattrStatus::kOK &&
|
|
|
|
uuid_string == target_uuid) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return base::FilePath();
|
|
|
|
}
|
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(
|
|
|
|
const UUID& uuid) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2017-04-13 10:12:28 -04:00
|
|
|
base::FilePath report_path =
|
|
|
|
LocateCrashReport(uuid, kReportStatePending | kReportStateCompleted);
|
2016-08-24 15:37:46 -04:00
|
|
|
if (report_path.empty())
|
|
|
|
return kReportNotFound;
|
|
|
|
|
|
|
|
base::ScopedFD lock(ObtainReportLock(report_path));
|
|
|
|
if (!lock.is_valid())
|
|
|
|
return kBusyError;
|
|
|
|
|
|
|
|
// If the crash report has already been uploaded, don't request new upload.
|
|
|
|
bool uploaded = false;
|
|
|
|
XattrStatus status =
|
|
|
|
ReadXattrBool(report_path, XattrName(kXattrIsUploaded), &uploaded);
|
|
|
|
if (status == XattrStatus::kOtherError)
|
|
|
|
return kDatabaseError;
|
|
|
|
if (uploaded)
|
|
|
|
return kCannotRequestUpload;
|
|
|
|
|
|
|
|
// Mark the crash report as having upload explicitly requested by the user,
|
|
|
|
// and move it to the pending state.
|
|
|
|
if (!WriteXattrBool(
|
|
|
|
report_path, XattrName(kXattrIsUploadExplicitlyRequested), true)) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
base::FilePath new_path =
|
|
|
|
base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName());
|
|
|
|
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
|
|
|
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
|
|
|
<< new_path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
2016-09-26 15:06:19 -07:00
|
|
|
Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);
|
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
// static
|
|
|
|
base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
|
|
|
|
const base::FilePath& path) {
|
2017-02-28 16:15:25 -05:00
|
|
|
int fd = HANDLE_EINTR(
|
|
|
|
open(path.value().c_str(),
|
|
|
|
O_RDONLY | O_NONBLOCK | O_EXLOCK | O_NOCTTY | O_CLOEXEC));
|
2015-01-26 15:59:01 -05:00
|
|
|
PLOG_IF(ERROR, fd < 0) << "open lock " << path.value();
|
|
|
|
return base::ScopedFD(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CrashReportDatabaseMac::ReadReportMetadataLocked(
|
|
|
|
const base::FilePath& path, Report* report) {
|
|
|
|
std::string uuid_string;
|
|
|
|
if (ReadXattr(path, XattrName(kXattrUUID),
|
|
|
|
&uuid_string) != XattrStatus::kOK ||
|
|
|
|
!report->uuid.InitializeFromString(uuid_string)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ReadXattrTimeT(path, XattrName(kXattrCreationTime),
|
|
|
|
&report->creation_time) != XattrStatus::kOK) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
report->id = std::string();
|
|
|
|
if (ReadXattr(path, XattrName(kXattrCollectorID),
|
|
|
|
&report->id) == XattrStatus::kOtherError) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
report->uploaded = false;
|
|
|
|
if (ReadXattrBool(path, XattrName(kXattrIsUploaded),
|
|
|
|
&report->uploaded) == XattrStatus::kOtherError) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
report->last_upload_attempt_time = 0;
|
|
|
|
if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime),
|
|
|
|
&report->last_upload_attempt_time) ==
|
|
|
|
XattrStatus::kOtherError) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
report->upload_attempts = 0;
|
|
|
|
if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount),
|
|
|
|
&report->upload_attempts) == XattrStatus::kOtherError) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
report->upload_explicitly_requested = false;
|
|
|
|
if (ReadXattrBool(path,
|
|
|
|
XattrName(kXattrIsUploadExplicitlyRequested),
|
|
|
|
&report->upload_explicitly_requested) ==
|
|
|
|
XattrStatus::kOtherError) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-11 12:17:05 -08:00
|
|
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::ReportsInDirectory(
|
2015-01-26 15:59:01 -05:00
|
|
|
const base::FilePath& path,
|
2015-03-06 18:43:28 -05:00
|
|
|
std::vector<CrashReportDatabase::Report>* reports) {
|
2015-03-09 18:47:52 -04:00
|
|
|
base::mac::ScopedNSAutoreleasePool pool;
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
DCHECK(reports->empty());
|
|
|
|
|
|
|
|
NSError* error = nil;
|
|
|
|
NSArray* paths = [[NSFileManager defaultManager]
|
|
|
|
contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value())
|
|
|
|
error:&error];
|
|
|
|
if (error) {
|
|
|
|
LOG(ERROR) << "Failed to enumerate reports in directory " << path.value()
|
|
|
|
<< ": " << [[error description] UTF8String];
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
|
|
|
reports->reserve([paths count]);
|
|
|
|
for (NSString* entry in paths) {
|
2015-03-19 18:41:01 -04:00
|
|
|
Report report;
|
|
|
|
report.file_path = path.Append([entry fileSystemRepresentation]);
|
|
|
|
base::ScopedFD lock(ObtainReportLock(report.file_path));
|
2015-01-26 15:59:01 -05:00
|
|
|
if (!lock.is_valid())
|
|
|
|
continue;
|
|
|
|
|
2015-03-19 18:41:01 -04:00
|
|
|
if (!ReadReportMetadataLocked(report.file_path, &report)) {
|
2015-01-26 15:59:01 -05:00
|
|
|
LOG(WARNING) << "Failed to read report metadata for "
|
2015-03-19 18:41:01 -04:00
|
|
|
<< report.file_path.value();
|
2015-01-26 15:59:01 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
reports->push_back(report);
|
|
|
|
}
|
|
|
|
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) {
|
2015-10-29 18:31:20 -04:00
|
|
|
return XattrNameInternal(name, xattr_new_names_);
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
2016-08-24 15:37:46 -04:00
|
|
|
CrashReportDatabase::OperationStatus
|
|
|
|
CrashReportDatabaseMac::MarkReportCompletedLocked(
|
|
|
|
const base::FilePath& report_path,
|
|
|
|
base::FilePath* out_path) {
|
|
|
|
if (RemoveXattr(report_path, XattrName(kXattrIsUploadExplicitlyRequested)) ==
|
|
|
|
XattrStatus::kOtherError) {
|
|
|
|
return kDatabaseError;
|
|
|
|
}
|
|
|
|
|
|
|
|
base::FilePath new_path =
|
|
|
|
base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
|
|
|
|
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
|
|
|
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
|
|
|
<< new_path.value();
|
|
|
|
return kFileSystemError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out_path)
|
|
|
|
*out_path = new_path;
|
|
|
|
return kNoError;
|
|
|
|
}
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<CrashReportDatabase> InitializeInternal(
|
|
|
|
const base::FilePath& path,
|
|
|
|
bool may_create) {
|
|
|
|
std::unique_ptr<CrashReportDatabaseMac> database_mac(
|
2015-10-08 13:10:02 -04:00
|
|
|
new CrashReportDatabaseMac(path));
|
|
|
|
if (!database_mac->Initialize(may_create))
|
|
|
|
database_mac.reset();
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
return std::unique_ptr<CrashReportDatabase>(database_mac.release());
|
2015-10-08 13:10:02 -04:00
|
|
|
}
|
|
|
|
|
2015-01-26 15:59:01 -05:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// static
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(
|
2015-01-26 15:59:01 -05:00
|
|
|
const base::FilePath& path) {
|
2015-10-08 13:10:02 -04:00
|
|
|
return InitializeInternal(path, true);
|
|
|
|
}
|
2015-01-26 15:59:01 -05:00
|
|
|
|
2015-10-08 13:10:02 -04:00
|
|
|
// static
|
2016-04-25 12:13:07 -07:00
|
|
|
std::unique_ptr<CrashReportDatabase>
|
|
|
|
CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) {
|
2015-10-08 13:10:02 -04:00
|
|
|
return InitializeInternal(path, false);
|
2015-01-26 15:59:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace crashpad
|