Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(959)

Side by Side Diff: client/settings.cc

Issue 999953002: Use LockFile/UnlockFile for Settings to port to Windows (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@lock-fileio-2
Patch Set: Tag inside, no info spam Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « client/settings.h ('k') | client/settings_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/settings.h" 15 #include "client/settings.h"
16 16
17 #include <limits> 17 #include <limits>
18 18
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <uuid/uuid.h>
22
23 #include "base/compiler_specific.h" 19 #include "base/compiler_specific.h"
24 #include "base/logging.h" 20 #include "base/logging.h"
25 #include "base/posix/eintr_wrapper.h" 21 #include "base/posix/eintr_wrapper.h"
26 #include "util/numeric/in_range_cast.h" 22 #include "util/numeric/in_range_cast.h"
27 23
28 namespace crashpad { 24 namespace crashpad {
29 25
26 namespace internal {
27
28 // static
29 FileHandle ScopedLockedFileHandleTraits::InvalidValue() {
30 return kInvalidFileHandle;
31 }
32
33 // static
34 void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
35 if (handle != kInvalidFileHandle) {
36 LoggingUnlockFile(handle);
37 CheckedCloseFile(handle);
38 }
39 }
40
41 } // namespace internal
42
30 struct ALIGNAS(4) Settings::Data { 43 struct ALIGNAS(4) Settings::Data {
31 static const uint32_t kSettingsMagic = 'CPds'; 44 static const uint32_t kSettingsMagic = 'CPds';
32 static const uint32_t kSettingsVersion = 1; 45 static const uint32_t kSettingsVersion = 1;
33 46
34 enum Options : uint32_t { 47 enum Options : uint32_t {
35 kUploadsEnabled = 1 << 0, 48 kUploadsEnabled = 1 << 0,
36 }; 49 };
37 50
38 Data() : magic(kSettingsMagic), 51 Data() : magic(kSettingsMagic),
39 version(kSettingsVersion), 52 version(kSettingsVersion),
(...skipping 14 matching lines...) Expand all
54 : file_path_(file_path), 67 : file_path_(file_path),
55 initialized_() { 68 initialized_() {
56 } 69 }
57 70
58 Settings::~Settings() { 71 Settings::~Settings() {
59 } 72 }
60 73
61 bool Settings::Initialize() { 74 bool Settings::Initialize() {
62 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); 75 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
63 76
64 ScopedFileHandle handle(HANDLE_EINTR(
65 open(file_path().value().c_str(),
66 O_CREAT | O_EXCL | O_WRONLY | O_EXLOCK,
67 0644)));
68
69 // The file was created, so this is a new database that needs to be
70 // initialized with a client ID.
71 if (handle.is_valid()) {
72 bool initialized = InitializeSettings(handle.get());
73 if (initialized)
74 INITIALIZATION_STATE_SET_VALID(initialized_);
75 return initialized;
76 }
77
78 // The file wasn't created, try opening it for a write operation. If the file
79 // needs to be recovered, writing is necessary. This also ensures that the
80 // process has permission to write the file.
81 Data settings; 77 Data settings;
82 if (!OpenForWritingAndReadSettings(&settings).is_valid()) 78 if (!OpenForWritingAndReadSettings(&settings).is_valid())
83 return false; 79 return false;
84 80
85 INITIALIZATION_STATE_SET_VALID(initialized_); 81 INITIALIZATION_STATE_SET_VALID(initialized_);
86 return true; 82 return true;
87 } 83 }
88 84
89 bool Settings::GetClientID(UUID* client_id) { 85 bool Settings::GetClientID(UUID* client_id) {
90 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 86 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
(...skipping 14 matching lines...) Expand all
105 return false; 101 return false;
106 102
107 *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0; 103 *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0;
108 return true; 104 return true;
109 } 105 }
110 106
111 bool Settings::SetUploadsEnabled(bool enabled) { 107 bool Settings::SetUploadsEnabled(bool enabled) {
112 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 108 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
113 109
114 Data settings; 110 Data settings;
115 ScopedFileHandle handle = OpenForWritingAndReadSettings(&settings); 111 ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
116 if (!handle.is_valid()) 112 if (!handle.is_valid())
117 return false; 113 return false;
118 114
119 if (enabled) 115 if (enabled)
120 settings.options |= Data::Options::kUploadsEnabled; 116 settings.options |= Data::Options::kUploadsEnabled;
121 else 117 else
122 settings.options &= ~Data::Options::kUploadsEnabled; 118 settings.options &= ~Data::Options::kUploadsEnabled;
123 119
124 return WriteSettings(handle.get(), settings); 120 return WriteSettings(handle.get(), settings);
125 } 121 }
126 122
127 bool Settings::GetLastUploadAttemptTime(time_t* time) { 123 bool Settings::GetLastUploadAttemptTime(time_t* time) {
128 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 124 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
129 125
130 Data settings; 126 Data settings;
131 if (!OpenAndReadSettings(&settings)) 127 if (!OpenAndReadSettings(&settings))
132 return false; 128 return false;
133 129
134 *time = InRangeCast<time_t>(settings.last_upload_attempt_time, 130 *time = InRangeCast<time_t>(settings.last_upload_attempt_time,
135 std::numeric_limits<time_t>::max()); 131 std::numeric_limits<time_t>::max());
136 return true; 132 return true;
137 } 133 }
138 134
139 bool Settings::SetLastUploadAttemptTime(time_t time) { 135 bool Settings::SetLastUploadAttemptTime(time_t time) {
140 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 136 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
141 137
142 Data settings; 138 Data settings;
143 ScopedFileHandle handle = OpenForWritingAndReadSettings(&settings); 139 ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
144 if (!handle.is_valid()) 140 if (!handle.is_valid())
145 return false; 141 return false;
146 142
147 settings.last_upload_attempt_time = InRangeCast<uint64_t>(time, 0); 143 settings.last_upload_attempt_time = InRangeCast<uint64_t>(time, 0);
148 144
149 return WriteSettings(handle.get(), settings); 145 return WriteSettings(handle.get(), settings);
150 } 146 }
151 147
152 ScopedFileHandle Settings::OpenForReading() { 148 // static
153 ScopedFileHandle handle(HANDLE_EINTR( 149 Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
154 open(file_path().value().c_str(), O_RDONLY | O_SHLOCK))); 150 FileHandle file,
155 PLOG_IF(ERROR, !handle.is_valid()) << "open for reading"; 151 FileLocking locking) {
156 return handle.Pass(); 152 ScopedFileHandle scoped(file);
153 if (scoped.is_valid()) {
154 if (!LoggingLockFile(scoped.get(), locking))
155 scoped.reset();
156 }
157 return ScopedLockedFileHandle(scoped.release());
157 } 158 }
158 159
159 ScopedFileHandle Settings::OpenForReadingAndWriting() { 160 Settings::ScopedLockedFileHandle Settings::OpenForReading() {
160 ScopedFileHandle handle(HANDLE_EINTR( 161 return MakeScopedLockedFileHandle(LoggingOpenFileForRead(file_path()),
161 open(file_path().value().c_str(), O_RDWR | O_EXLOCK | O_CREAT, 0644))); 162 FileLocking::kShared);
162 PLOG_IF(ERROR, !handle.is_valid()) << "open for writing"; 163 }
163 return handle.Pass(); 164
165 Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting() {
166 return MakeScopedLockedFileHandle(
167 LoggingOpenFileForReadAndWrite(file_path(),
168 FileWriteMode::kReuseOrCreate,
169 FilePermissions::kWorldReadable),
170 FileLocking::kExclusive);
164 } 171 }
165 172
166 bool Settings::OpenAndReadSettings(Data* out_data) { 173 bool Settings::OpenAndReadSettings(Data* out_data) {
167 ScopedFileHandle handle = OpenForReading(); 174 ScopedLockedFileHandle handle = OpenForReading();
168 if (!handle.is_valid()) 175 if (!handle.is_valid())
169 return false; 176 return false;
170 177
171 if (ReadSettings(handle.get(), out_data)) 178 if (ReadSettings(handle.get(), out_data))
172 return true; 179 return true;
173 180
174 // The settings file is corrupt, so reinitialize it. 181 // The settings file is corrupt, so reinitialize it.
175 handle.reset(); 182 handle.reset();
176 183
177 // The settings failed to be read, so re-initialize them. 184 // The settings failed to be read, so re-initialize them.
178 return RecoverSettings(kInvalidFileHandle, out_data); 185 return RecoverSettings(kInvalidFileHandle, out_data);
179 } 186 }
180 187
181 ScopedFileHandle Settings::OpenForWritingAndReadSettings(Data* out_data) { 188 Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings(
182 ScopedFileHandle handle = OpenForReadingAndWriting(); 189 Data* out_data) {
190 ScopedLockedFileHandle handle = OpenForReadingAndWriting();
183 if (!handle.is_valid()) 191 if (!handle.is_valid())
184 return ScopedFileHandle(); 192 return ScopedLockedFileHandle();
185 193
186 if (!ReadSettings(handle.get(), out_data)) { 194 if (!ReadSettings(handle.get(), out_data)) {
187 if (!RecoverSettings(handle.get(), out_data)) 195 if (!RecoverSettings(handle.get(), out_data))
188 return ScopedFileHandle(); 196 return ScopedLockedFileHandle();
189 } 197 }
190 198
191 return handle.Pass(); 199 return handle.Pass();
192 } 200 }
193 201
194 bool Settings::ReadSettings(FileHandle handle, Data* out_data) { 202 bool Settings::ReadSettings(FileHandle handle, Data* out_data) {
195 if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) 203 if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
196 return false; 204 return false;
197 205
198 if (!LoggingReadFile(handle, out_data, sizeof(*out_data))) 206 if (!LoggingReadFile(handle, out_data, sizeof(*out_data)))
199 return false; 207 return false;
200 208
201 if (out_data->magic != Data::kSettingsMagic) { 209 if (out_data->magic != Data::kSettingsMagic) {
202 LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic; 210 LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic;
203 return false; 211 return false;
204 } 212 }
205 213
206 if (out_data->version != Data::kSettingsVersion) { 214 if (out_data->version != Data::kSettingsVersion) {
207 LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion; 215 LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion;
208 return false; 216 return false;
209 } 217 }
210 218
211 return true; 219 return true;
212 } 220 }
213 221
214 bool Settings::WriteSettings(FileHandle handle, const Data& data) { 222 bool Settings::WriteSettings(FileHandle handle, const Data& data) {
215 if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) 223 if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
216 return false; 224 return false;
217 225
218 if (HANDLE_EINTR(ftruncate(handle, 0)) != 0) { 226 if (!LoggingTruncateFile(handle))
219 PLOG(ERROR) << "ftruncate settings file";
220 return false; 227 return false;
221 }
222 228
223 return LoggingWriteFile(handle, &data, sizeof(Data)); 229 return LoggingWriteFile(handle, &data, sizeof(Data));
224 } 230 }
225 231
226 bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { 232 bool Settings::RecoverSettings(FileHandle handle, Data* out_data) {
227 ScopedFileHandle scoped_handle; 233 ScopedLockedFileHandle scoped_handle;
228 if (handle == kInvalidFileHandle) { 234 if (handle == kInvalidFileHandle) {
229 scoped_handle = OpenForReadingAndWriting(); 235 scoped_handle = OpenForReadingAndWriting();
230 handle = scoped_handle.get(); 236 handle = scoped_handle.get();
231 237
232 // Test if the file has already been recovered now that the exclusive lock 238 // Test if the file has already been recovered now that the exclusive lock
233 // is held. 239 // is held.
234 if (ReadSettings(handle, out_data)) 240 if (ReadSettings(handle, out_data))
235 return true; 241 return true;
236 } 242 }
237 243
238 LOG(INFO) << "Recovering settings file " << file_path().value();
239
240 if (handle == kInvalidFileHandle) { 244 if (handle == kInvalidFileHandle) {
241 LOG(ERROR) << "Invalid file handle"; 245 LOG(ERROR) << "Invalid file handle";
242 return false; 246 return false;
243 } 247 }
244 248
245 if (!InitializeSettings(handle)) 249 if (!InitializeSettings(handle))
246 return false; 250 return false;
247 251
248 return ReadSettings(handle, out_data); 252 return ReadSettings(handle, out_data);
249 } 253 }
250 254
251 bool Settings::InitializeSettings(FileHandle handle) { 255 bool Settings::InitializeSettings(FileHandle handle) {
252 uuid_t uuid;
253 uuid_generate(uuid);
254
255 Data settings; 256 Data settings;
256 settings.client_id.InitializeFromBytes(uuid); 257 if (!settings.client_id.InitializeWithNew())
258 return false;
257 259
258 return WriteSettings(handle, settings); 260 return WriteSettings(handle, settings);
259 } 261 }
260 262
261 } // namespace crashpad 263 } // namespace crashpad
OLDNEW
« no previous file with comments | « client/settings.h ('k') | client/settings_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698