2022-09-06 19:14:07 -04:00
|
|
|
|
// Copyright 2015 The Crashpad Authors
|
2015-03-09 18:47:52 -04:00
|
|
|
|
//
|
|
|
|
|
// 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/settings.h"
|
|
|
|
|
|
2016-01-06 12:22:50 -05:00
|
|
|
|
#include <stdint.h>
|
2024-10-21 14:52:29 +11:00
|
|
|
|
#include <string.h>
|
2016-01-06 12:22:50 -05:00
|
|
|
|
|
2015-03-09 18:47:52 -04:00
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
2022-11-07 11:48:16 -08:00
|
|
|
|
#include "base/notreached.h"
|
2015-03-09 18:47:52 -04:00
|
|
|
|
#include "base/posix/eintr_wrapper.h"
|
2022-01-19 15:00:24 -05:00
|
|
|
|
#include "build/build_config.h"
|
2018-02-16 12:24:09 -08:00
|
|
|
|
#include "util/file/filesystem.h"
|
2015-03-09 18:47:52 -04:00
|
|
|
|
#include "util/numeric/in_range_cast.h"
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
2022-11-03 15:06:18 -07:00
|
|
|
|
#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
2018-02-16 12:24:09 -08:00
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::ScopedLockedFileHandle()
|
|
|
|
|
: handle_(kInvalidFileHandle), lockfile_path_() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::ScopedLockedFileHandle(
|
|
|
|
|
FileHandle handle,
|
|
|
|
|
const base::FilePath& lockfile_path)
|
|
|
|
|
: handle_(handle), lockfile_path_(lockfile_path) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::ScopedLockedFileHandle(
|
|
|
|
|
ScopedLockedFileHandle&& other)
|
|
|
|
|
: handle_(other.handle_), lockfile_path_(other.lockfile_path_) {
|
|
|
|
|
other.handle_ = kInvalidFileHandle;
|
|
|
|
|
other.lockfile_path_ = base::FilePath();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=(
|
|
|
|
|
ScopedLockedFileHandle&& other) {
|
|
|
|
|
handle_ = other.handle_;
|
|
|
|
|
lockfile_path_ = other.lockfile_path_;
|
|
|
|
|
|
|
|
|
|
other.handle_ = kInvalidFileHandle;
|
|
|
|
|
other.lockfile_path_ = base::FilePath();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::~ScopedLockedFileHandle() {
|
|
|
|
|
Destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::ScopedLockedFileHandle::Destroy() {
|
|
|
|
|
if (handle_ != kInvalidFileHandle) {
|
|
|
|
|
CheckedCloseFile(handle_);
|
|
|
|
|
}
|
|
|
|
|
if (!lockfile_path_.empty()) {
|
2018-12-05 19:58:48 -08:00
|
|
|
|
const bool success = LoggingRemoveFile(lockfile_path_);
|
|
|
|
|
DCHECK(success);
|
2018-02-16 12:24:09 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 15:06:18 -07:00
|
|
|
|
#else // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
2018-02-16 12:24:09 -08:00
|
|
|
|
|
2022-04-07 19:52:07 -04:00
|
|
|
|
#if BUILDFLAG(IS_IOS)
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::ScopedLockedFileHandle(
|
|
|
|
|
const FileHandle& value)
|
|
|
|
|
: ScopedGeneric(value) {
|
|
|
|
|
ios_background_task_ = std::make_unique<internal::ScopedBackgroundTask>(
|
|
|
|
|
"ScopedLockedFileHandle");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle::ScopedLockedFileHandle(
|
|
|
|
|
Settings::ScopedLockedFileHandle&& rvalue) {
|
|
|
|
|
ios_background_task_.reset(rvalue.ios_background_task_.release());
|
|
|
|
|
reset(rvalue.release());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Settings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=(
|
|
|
|
|
Settings::ScopedLockedFileHandle&& rvalue) {
|
|
|
|
|
ios_background_task_.reset(rvalue.ios_background_task_.release());
|
|
|
|
|
reset(rvalue.release());
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 13:59:02 -04:00
|
|
|
|
Settings::ScopedLockedFileHandle::~ScopedLockedFileHandle() {
|
|
|
|
|
// Call reset() to ensure the lock is released before the ios_background_task.
|
|
|
|
|
reset();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 19:52:07 -04:00
|
|
|
|
#endif // BUILDFLAG(IS_IOS)
|
|
|
|
|
|
2015-04-20 14:21:48 -07:00
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
|
|
|
|
|
if (handle != kInvalidFileHandle) {
|
|
|
|
|
LoggingUnlockFile(handle);
|
|
|
|
|
CheckedCloseFile(handle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
2022-01-19 15:00:24 -05:00
|
|
|
|
#endif // BUILDFLAG(IS_FUCHSIA)
|
2018-02-16 12:24:09 -08:00
|
|
|
|
|
2015-09-21 10:51:15 -07:00
|
|
|
|
struct Settings::Data {
|
2020-04-14 09:59:21 -04:00
|
|
|
|
static constexpr uint32_t kSettingsMagic = 'CPds';
|
2024-10-21 14:52:29 +11:00
|
|
|
|
|
|
|
|
|
// Version number only used for incompatible changes to Data. Do not change
|
|
|
|
|
// this when adding additional fields at the end. Modifying `kSettingsVersion`
|
|
|
|
|
// will wipe away the entire struct when reading from other versions.
|
2020-04-14 09:59:21 -04:00
|
|
|
|
static constexpr uint32_t kSettingsVersion = 1;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2015-03-10 13:52:19 -04:00
|
|
|
|
enum Options : uint32_t {
|
|
|
|
|
kUploadsEnabled = 1 << 0,
|
|
|
|
|
};
|
|
|
|
|
|
2015-03-12 19:34:05 -04:00
|
|
|
|
Data() : magic(kSettingsMagic),
|
|
|
|
|
version(kSettingsVersion),
|
2015-03-09 18:47:52 -04:00
|
|
|
|
options(0),
|
2015-03-12 19:34:05 -04:00
|
|
|
|
padding_0(0),
|
2015-03-09 18:47:52 -04:00
|
|
|
|
last_upload_attempt_time(0),
|
|
|
|
|
client_id() {}
|
|
|
|
|
|
2015-03-12 19:34:05 -04:00
|
|
|
|
uint32_t magic;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
uint32_t version;
|
|
|
|
|
uint32_t options;
|
2015-03-12 19:34:05 -04:00
|
|
|
|
uint32_t padding_0;
|
2016-11-15 13:50:32 -05:00
|
|
|
|
int64_t last_upload_attempt_time; // time_t
|
2015-03-09 18:47:52 -04:00
|
|
|
|
UUID client_id;
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-13 14:25:14 -08:00
|
|
|
|
Settings::Settings() = default;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2018-02-13 14:25:14 -08:00
|
|
|
|
Settings::~Settings() = default;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2018-02-13 14:25:14 -08:00
|
|
|
|
bool Settings::Initialize(const base::FilePath& file_path) {
|
|
|
|
|
DCHECK(initialized_.is_uninitialized());
|
2015-10-07 16:20:29 -04:00
|
|
|
|
initialized_.set_invalid();
|
2018-02-13 14:25:14 -08:00
|
|
|
|
file_path_ = file_path;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
|
|
|
|
if (!OpenForWritingAndReadSettings(&settings).is_valid())
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
initialized_.set_valid();
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::GetClientID(UUID* client_id) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
DCHECK(initialized_.is_valid());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
|
|
|
|
if (!OpenAndReadSettings(&settings))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
*client_id = settings.client_id;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::GetUploadsEnabled(bool* enabled) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
DCHECK(initialized_.is_valid());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
|
|
|
|
if (!OpenAndReadSettings(&settings))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
*enabled = (settings.options & Data::Options::kUploadsEnabled) != 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::SetUploadsEnabled(bool enabled) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
DCHECK(initialized_.is_valid());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
2015-04-20 14:21:48 -07:00
|
|
|
|
ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (!handle.is_valid())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
|
settings.options |= Data::Options::kUploadsEnabled;
|
|
|
|
|
else
|
|
|
|
|
settings.options &= ~Data::Options::kUploadsEnabled;
|
|
|
|
|
|
|
|
|
|
return WriteSettings(handle.get(), settings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::GetLastUploadAttemptTime(time_t* time) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
DCHECK(initialized_.is_valid());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
|
|
|
|
if (!OpenAndReadSettings(&settings))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
*time = InRangeCast<time_t>(settings.last_upload_attempt_time,
|
|
|
|
|
std::numeric_limits<time_t>::max());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::SetLastUploadAttemptTime(time_t time) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
DCHECK(initialized_.is_valid());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
Data settings;
|
2015-04-20 14:21:48 -07:00
|
|
|
|
ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (!handle.is_valid())
|
|
|
|
|
return false;
|
|
|
|
|
|
2016-11-15 13:50:32 -05:00
|
|
|
|
settings.last_upload_attempt_time = InRangeCast<int64_t>(time, 0);
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
return WriteSettings(handle.get(), settings);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 15:06:18 -07:00
|
|
|
|
#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
|
|
|
|
// static
|
|
|
|
|
bool Settings::IsLockExpired(const base::FilePath& file_path,
|
|
|
|
|
time_t lockfile_ttl) {
|
|
|
|
|
time_t now = time(nullptr);
|
|
|
|
|
base::FilePath lock_path(file_path.value() + Settings::kLockfileExtension);
|
|
|
|
|
ScopedFileHandle lock_fd(LoggingOpenFileForRead(lock_path));
|
|
|
|
|
time_t lock_timestamp;
|
|
|
|
|
if (!LoggingReadFileExactly(
|
|
|
|
|
lock_fd.get(), &lock_timestamp, sizeof(lock_timestamp))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return now >= lock_timestamp + lockfile_ttl;
|
|
|
|
|
}
|
|
|
|
|
#endif // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
|
|
|
|
|
2015-04-20 14:21:48 -07:00
|
|
|
|
// static
|
|
|
|
|
Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
|
2022-11-03 15:06:18 -07:00
|
|
|
|
const internal::MakeScopedLockedFileHandleOptions& options,
|
2018-02-16 12:24:09 -08:00
|
|
|
|
FileLocking locking,
|
|
|
|
|
const base::FilePath& file_path) {
|
2022-11-03 15:06:18 -07:00
|
|
|
|
#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
|
|
|
|
base::FilePath lockfile_path(file_path.value() +
|
|
|
|
|
Settings::kLockfileExtension);
|
|
|
|
|
ScopedFileHandle lockfile_scoped(
|
|
|
|
|
LoggingOpenFileForWrite(lockfile_path,
|
|
|
|
|
FileWriteMode::kCreateOrFail,
|
|
|
|
|
FilePermissions::kWorldReadable));
|
|
|
|
|
if (!lockfile_scoped.is_valid()) {
|
|
|
|
|
return ScopedLockedFileHandle();
|
|
|
|
|
}
|
|
|
|
|
time_t now = time(nullptr);
|
|
|
|
|
if (!LoggingWriteFile(lockfile_scoped.get(), &now, sizeof(now))) {
|
|
|
|
|
return ScopedLockedFileHandle();
|
|
|
|
|
}
|
|
|
|
|
ScopedFileHandle scoped(GetHandleFromOptions(file_path, options));
|
2018-02-16 12:24:09 -08:00
|
|
|
|
if (scoped.is_valid()) {
|
|
|
|
|
return ScopedLockedFileHandle(scoped.release(), lockfile_path);
|
|
|
|
|
}
|
2022-11-03 15:06:18 -07:00
|
|
|
|
bool success = LoggingRemoveFile(lockfile_path);
|
|
|
|
|
DCHECK(success);
|
|
|
|
|
return ScopedLockedFileHandle();
|
2018-02-16 12:24:09 -08:00
|
|
|
|
#else
|
2022-11-03 15:06:18 -07:00
|
|
|
|
ScopedFileHandle scoped(GetHandleFromOptions(file_path, options));
|
2022-04-07 19:52:07 -04:00
|
|
|
|
// It's important to create the ScopedLockedFileHandle before calling
|
|
|
|
|
// LoggingLockFile on iOS, so a ScopedBackgroundTask is created *before*
|
|
|
|
|
// the LoggingLockFile call below.
|
|
|
|
|
ScopedLockedFileHandle handle(kInvalidFileHandle);
|
2015-04-20 14:21:48 -07:00
|
|
|
|
if (scoped.is_valid()) {
|
2021-05-05 14:15:49 -04:00
|
|
|
|
if (LoggingLockFile(
|
|
|
|
|
scoped.get(), locking, FileLockingBlocking::kBlocking) !=
|
|
|
|
|
FileLockingResult::kSuccess) {
|
2015-04-20 14:21:48 -07:00
|
|
|
|
scoped.reset();
|
2021-05-05 14:15:49 -04:00
|
|
|
|
}
|
2015-04-20 14:21:48 -07:00
|
|
|
|
}
|
2022-04-07 19:52:07 -04:00
|
|
|
|
handle.reset(scoped.release());
|
|
|
|
|
return handle;
|
2022-11-03 15:06:18 -07:00
|
|
|
|
#endif // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
FileHandle Settings::GetHandleFromOptions(
|
|
|
|
|
const base::FilePath& file_path,
|
|
|
|
|
const internal::MakeScopedLockedFileHandleOptions& options) {
|
|
|
|
|
switch (options.function_enum) {
|
|
|
|
|
case internal::FileOpenFunction::kLoggingOpenFileForRead:
|
|
|
|
|
return LoggingOpenFileForRead(file_path);
|
|
|
|
|
case internal::FileOpenFunction::kLoggingOpenFileForReadAndWrite:
|
|
|
|
|
return LoggingOpenFileForReadAndWrite(
|
|
|
|
|
file_path, options.mode, options.permissions);
|
|
|
|
|
case internal::FileOpenFunction::kOpenFileForReadAndWrite:
|
|
|
|
|
return OpenFileForReadAndWrite(
|
|
|
|
|
file_path, options.mode, options.permissions);
|
|
|
|
|
}
|
2024-08-14 13:09:43 +10:00
|
|
|
|
NOTREACHED();
|
2015-03-09 18:47:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 14:21:48 -07:00
|
|
|
|
Settings::ScopedLockedFileHandle Settings::OpenForReading() {
|
2022-11-03 15:06:18 -07:00
|
|
|
|
internal::MakeScopedLockedFileHandleOptions options;
|
|
|
|
|
options.function_enum = internal::FileOpenFunction::kLoggingOpenFileForRead;
|
|
|
|
|
return MakeScopedLockedFileHandle(options, FileLocking::kShared, file_path());
|
2015-04-20 14:21:48 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting(
|
|
|
|
|
FileWriteMode mode, bool log_open_error) {
|
|
|
|
|
DCHECK(mode != FileWriteMode::kTruncateOrCreate);
|
|
|
|
|
|
2022-11-03 15:06:18 -07:00
|
|
|
|
internal::MakeScopedLockedFileHandleOptions options;
|
|
|
|
|
options.mode = mode;
|
|
|
|
|
options.permissions = FilePermissions::kOwnerOnly;
|
2015-10-07 16:20:29 -04:00
|
|
|
|
if (log_open_error) {
|
2022-11-03 15:06:18 -07:00
|
|
|
|
options.function_enum =
|
|
|
|
|
internal::FileOpenFunction::kLoggingOpenFileForReadAndWrite;
|
2015-10-07 16:20:29 -04:00
|
|
|
|
} else {
|
2022-11-03 15:06:18 -07:00
|
|
|
|
options.function_enum =
|
|
|
|
|
internal::FileOpenFunction::kOpenFileForReadAndWrite;
|
2015-10-07 16:20:29 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-16 12:24:09 -08:00
|
|
|
|
return MakeScopedLockedFileHandle(
|
2022-11-03 15:06:18 -07:00
|
|
|
|
options, FileLocking::kExclusive, file_path());
|
2015-03-09 18:47:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::OpenAndReadSettings(Data* out_data) {
|
2015-04-20 14:21:48 -07:00
|
|
|
|
ScopedLockedFileHandle handle = OpenForReading();
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (!handle.is_valid())
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
if (ReadSettings(handle.get(), out_data, true))
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// The settings file is corrupt, so reinitialize it.
|
|
|
|
|
handle.reset();
|
|
|
|
|
|
|
|
|
|
// The settings failed to be read, so re-initialize them.
|
|
|
|
|
return RecoverSettings(kInvalidFileHandle, out_data);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 14:21:48 -07:00
|
|
|
|
Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings(
|
|
|
|
|
Data* out_data) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
ScopedLockedFileHandle handle;
|
|
|
|
|
bool created = false;
|
|
|
|
|
if (!initialized_.is_valid()) {
|
|
|
|
|
// If this object is initializing, it hasn’t seen a settings file already,
|
|
|
|
|
// so go easy on errors. Creating a new settings file for the first time
|
|
|
|
|
// shouldn’t spew log messages.
|
|
|
|
|
//
|
|
|
|
|
// First, try to use an existing settings file.
|
|
|
|
|
handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrFail, false);
|
|
|
|
|
|
|
|
|
|
if (!handle.is_valid()) {
|
|
|
|
|
// Create a new settings file if it didn’t already exist.
|
|
|
|
|
handle = OpenForReadingAndWriting(FileWriteMode::kCreateOrFail, false);
|
|
|
|
|
|
|
|
|
|
if (handle.is_valid()) {
|
|
|
|
|
created = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There may have been a race to create the file, and something else may
|
|
|
|
|
// have won. There will be one more attempt to try to open or create the
|
|
|
|
|
// file below.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!handle.is_valid()) {
|
|
|
|
|
// Either the object is initialized, meaning it’s already seen a valid
|
|
|
|
|
// settings file, or the object is initializing and none of the above
|
|
|
|
|
// attempts to create the settings file succeeded. Either way, this is the
|
|
|
|
|
// last chance for success, so if this fails, log a message.
|
|
|
|
|
handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (!handle.is_valid())
|
2015-04-20 14:21:48 -07:00
|
|
|
|
return ScopedLockedFileHandle();
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
// Attempt reading the settings even if the file is known to have just been
|
|
|
|
|
// created. The file-create and file-lock operations don’t occur atomically,
|
|
|
|
|
// and something else may have written the settings before this invocation
|
|
|
|
|
// took the lock. If the settings file was definitely just created, though,
|
|
|
|
|
// don’t log any read errors. The expected non-race behavior in this case is a
|
|
|
|
|
// zero-length read, with ReadSettings() failing.
|
|
|
|
|
if (!ReadSettings(handle.get(), out_data, !created)) {
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (!RecoverSettings(handle.get(), out_data))
|
2015-04-20 14:21:48 -07:00
|
|
|
|
return ScopedLockedFileHandle();
|
2015-03-09 18:47:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-30 14:20:54 -08:00
|
|
|
|
return handle;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
bool Settings::ReadSettings(FileHandle handle,
|
|
|
|
|
Data* out_data,
|
|
|
|
|
bool log_read_error) {
|
2024-10-21 14:52:29 +11:00
|
|
|
|
if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) {
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return false;
|
2024-10-21 14:52:29 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This clears `out_data` so that any bytes not read from disk are zero
|
|
|
|
|
// initialized. This is expected when reading from an older settings file with
|
|
|
|
|
// fewer fields.
|
|
|
|
|
memset(out_data, 0, sizeof(*out_data));
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2024-10-21 14:52:29 +11:00
|
|
|
|
const FileOperationResult read_result =
|
|
|
|
|
log_read_error ? LoggingReadFileUntil(handle, out_data, sizeof(*out_data))
|
|
|
|
|
: ReadFileUntil(handle, out_data, sizeof(*out_data));
|
2015-10-07 16:20:29 -04:00
|
|
|
|
|
2024-10-21 14:52:29 +11:00
|
|
|
|
if (read_result <= 0) {
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return false;
|
2024-10-21 14:52:29 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Newer versions of crashpad may add fields to Data, but all versions have
|
|
|
|
|
// the data members up to `client_id`. Do not attempt to understand a smaller
|
|
|
|
|
// struct read.
|
|
|
|
|
const size_t min_size =
|
|
|
|
|
offsetof(Data, client_id) + sizeof(out_data->client_id);
|
|
|
|
|
if (static_cast<size_t>(read_result) < min_size) {
|
|
|
|
|
LOG(ERROR) << "Settings file too small: minimum " << min_size
|
|
|
|
|
<< ", observed " << read_result;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
2015-03-12 19:34:05 -04:00
|
|
|
|
if (out_data->magic != Data::kSettingsMagic) {
|
|
|
|
|
LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (out_data->version != Data::kSettingsVersion) {
|
|
|
|
|
LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::WriteSettings(FileHandle handle, const Data& data) {
|
|
|
|
|
if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-04-20 14:21:48 -07:00
|
|
|
|
if (!LoggingTruncateFile(handle))
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return LoggingWriteFile(handle, &data, sizeof(Data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::RecoverSettings(FileHandle handle, Data* out_data) {
|
2015-04-20 14:21:48 -07:00
|
|
|
|
ScopedLockedFileHandle scoped_handle;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
if (handle == kInvalidFileHandle) {
|
2015-10-07 16:20:29 -04:00
|
|
|
|
scoped_handle =
|
|
|
|
|
OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);
|
2015-03-09 18:47:52 -04:00
|
|
|
|
handle = scoped_handle.get();
|
|
|
|
|
|
|
|
|
|
// Test if the file has already been recovered now that the exclusive lock
|
|
|
|
|
// is held.
|
2015-10-07 16:20:29 -04:00
|
|
|
|
if (ReadSettings(handle, out_data, true))
|
2015-03-09 18:47:52 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (handle == kInvalidFileHandle) {
|
|
|
|
|
LOG(ERROR) << "Invalid file handle";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!InitializeSettings(handle))
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-10-07 16:20:29 -04:00
|
|
|
|
return ReadSettings(handle, out_data, true);
|
2015-03-09 18:47:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Settings::InitializeSettings(FileHandle handle) {
|
|
|
|
|
Data settings;
|
2015-04-20 14:21:48 -07:00
|
|
|
|
if (!settings.client_id.InitializeWithNew())
|
|
|
|
|
return false;
|
2015-03-09 18:47:52 -04:00
|
|
|
|
|
|
|
|
|
return WriteSettings(handle, settings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace crashpad
|