Index: client/settings.cc |
diff --git a/client/settings.cc b/client/settings.cc |
index 9467a52378b92ed675c18feaf837b00d8514fd42..7f126ad70d12e79d3ae70a4f1cadcd14e4d908d5 100644 |
--- a/client/settings.cc |
+++ b/client/settings.cc |
@@ -71,18 +71,18 @@ Settings::~Settings() { |
} |
bool Settings::Initialize() { |
- INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
+ initialized_.set_invalid(); |
Data settings; |
if (!OpenForWritingAndReadSettings(&settings).is_valid()) |
return false; |
- INITIALIZATION_STATE_SET_VALID(initialized_); |
+ initialized_.set_valid(); |
return true; |
} |
bool Settings::GetClientID(UUID* client_id) { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ DCHECK(initialized_.is_valid()); |
Data settings; |
if (!OpenAndReadSettings(&settings)) |
@@ -93,7 +93,7 @@ bool Settings::GetClientID(UUID* client_id) { |
} |
bool Settings::GetUploadsEnabled(bool* enabled) { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ DCHECK(initialized_.is_valid()); |
Data settings; |
if (!OpenAndReadSettings(&settings)) |
@@ -104,7 +104,7 @@ bool Settings::GetUploadsEnabled(bool* enabled) { |
} |
bool Settings::SetUploadsEnabled(bool enabled) { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ DCHECK(initialized_.is_valid()); |
Data settings; |
ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); |
@@ -120,7 +120,7 @@ bool Settings::SetUploadsEnabled(bool enabled) { |
} |
bool Settings::GetLastUploadAttemptTime(time_t* time) { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ DCHECK(initialized_.is_valid()); |
Data settings; |
if (!OpenAndReadSettings(&settings)) |
@@ -132,7 +132,7 @@ bool Settings::GetLastUploadAttemptTime(time_t* time) { |
} |
bool Settings::SetLastUploadAttemptTime(time_t time) { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ DCHECK(initialized_.is_valid()); |
Data settings; |
ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); |
@@ -161,12 +161,20 @@ Settings::ScopedLockedFileHandle Settings::OpenForReading() { |
FileLocking::kShared); |
} |
-Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting() { |
- return MakeScopedLockedFileHandle( |
- LoggingOpenFileForReadAndWrite(file_path(), |
- FileWriteMode::kReuseOrCreate, |
- FilePermissions::kWorldReadable), |
- FileLocking::kExclusive); |
+Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting( |
+ FileWriteMode mode, bool log_open_error) { |
+ DCHECK(mode != FileWriteMode::kTruncateOrCreate); |
+ |
+ FileHandle handle; |
+ if (log_open_error) { |
+ handle = LoggingOpenFileForReadAndWrite( |
+ file_path(), mode, FilePermissions::kWorldReadable); |
+ } else { |
+ handle = OpenFileForReadAndWrite( |
+ file_path(), mode, FilePermissions::kWorldReadable); |
+ } |
+ |
+ return MakeScopedLockedFileHandle(handle, FileLocking::kExclusive); |
} |
bool Settings::OpenAndReadSettings(Data* out_data) { |
@@ -174,7 +182,7 @@ bool Settings::OpenAndReadSettings(Data* out_data) { |
if (!handle.is_valid()) |
return false; |
- if (ReadSettings(handle.get(), out_data)) |
+ if (ReadSettings(handle.get(), out_data, true)) |
return true; |
// The settings file is corrupt, so reinitialize it. |
@@ -186,11 +194,48 @@ bool Settings::OpenAndReadSettings(Data* out_data) { |
Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings( |
Data* out_data) { |
- ScopedLockedFileHandle handle = OpenForReadingAndWriting(); |
+ 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); |
+ } |
+ |
if (!handle.is_valid()) |
return ScopedLockedFileHandle(); |
- if (!ReadSettings(handle.get(), out_data)) { |
+ // 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)) { |
if (!RecoverSettings(handle.get(), out_data)) |
return ScopedLockedFileHandle(); |
} |
@@ -198,11 +243,19 @@ Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings( |
return handle.Pass(); |
} |
-bool Settings::ReadSettings(FileHandle handle, Data* out_data) { |
+bool Settings::ReadSettings(FileHandle handle, |
+ Data* out_data, |
+ bool log_read_error) { |
if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) |
return false; |
- if (!LoggingReadFile(handle, out_data, sizeof(*out_data))) |
+ bool read_result; |
+ if (log_read_error) |
+ read_result = LoggingReadFile(handle, out_data, sizeof(*out_data)); |
+ else |
+ read_result = ReadFile(handle, out_data, sizeof(*out_data)); |
+ |
+ if (!read_result) |
return false; |
if (out_data->magic != Data::kSettingsMagic) { |
@@ -231,12 +284,13 @@ bool Settings::WriteSettings(FileHandle handle, const Data& data) { |
bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { |
ScopedLockedFileHandle scoped_handle; |
if (handle == kInvalidFileHandle) { |
- scoped_handle = OpenForReadingAndWriting(); |
+ scoped_handle = |
+ OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true); |
handle = scoped_handle.get(); |
// Test if the file has already been recovered now that the exclusive lock |
// is held. |
- if (ReadSettings(handle, out_data)) |
+ if (ReadSettings(handle, out_data, true)) |
return true; |
} |
@@ -248,7 +302,7 @@ bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { |
if (!InitializeSettings(handle)) |
return false; |
- return ReadSettings(handle, out_data); |
+ return ReadSettings(handle, out_data, true); |
} |
bool Settings::InitializeSettings(FileHandle handle) { |