settings.cc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. #include "client/settings.h"
  15. #include <stdint.h>
  16. #include <limits>
  17. #include "base/logging.h"
  18. #include "base/posix/eintr_wrapper.h"
  19. #include "util/numeric/in_range_cast.h"
  20. namespace crashpad {
  21. namespace internal {
  22. // static
  23. void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
  24. if (handle != kInvalidFileHandle) {
  25. LoggingUnlockFile(handle);
  26. CheckedCloseFile(handle);
  27. }
  28. }
  29. } // namespace internal
  30. struct Settings::Data {
  31. static const uint32_t kSettingsMagic = 'CPds';
  32. static const uint32_t kSettingsVersion = 1;
  33. enum Options : uint32_t {
  34. kUploadsEnabled = 1 << 0,
  35. };
  36. Data() : magic(kSettingsMagic),
  37. version(kSettingsVersion),
  38. options(0),
  39. padding_0(0),
  40. last_upload_attempt_time(0),
  41. client_id() {}
  42. uint32_t magic;
  43. uint32_t version;
  44. uint32_t options;
  45. uint32_t padding_0;
  46. int64_t last_upload_attempt_time; // time_t
  47. UUID client_id;
  48. };
  49. Settings::Settings(const base::FilePath& file_path)
  50. : file_path_(file_path),
  51. initialized_() {
  52. }
  53. Settings::~Settings() {
  54. }
  55. bool Settings::Initialize() {
  56. initialized_.set_invalid();
  57. Data settings;
  58. if (!OpenForWritingAndReadSettings(&settings).is_valid())
  59. return false;
  60. initialized_.set_valid();
  61. return true;
  62. }
  63. bool Settings::GetClientID(UUID* client_id) {
  64. DCHECK(initialized_.is_valid());
  65. Data settings;
  66. if (!OpenAndReadSettings(&settings))
  67. return false;
  68. *client_id = settings.client_id;
  69. return true;
  70. }
  71. bool Settings::GetUploadsEnabled(bool* enabled) {
  72. DCHECK(initialized_.is_valid());
  73. Data settings;
  74. if (!OpenAndReadSettings(&settings))
  75. return false;
  76. *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0;
  77. return true;
  78. }
  79. bool Settings::SetUploadsEnabled(bool enabled) {
  80. DCHECK(initialized_.is_valid());
  81. Data settings;
  82. ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
  83. if (!handle.is_valid())
  84. return false;
  85. if (enabled)
  86. settings.options |= Data::Options::kUploadsEnabled;
  87. else
  88. settings.options &= ~Data::Options::kUploadsEnabled;
  89. return WriteSettings(handle.get(), settings);
  90. }
  91. bool Settings::GetLastUploadAttemptTime(time_t* time) {
  92. DCHECK(initialized_.is_valid());
  93. Data settings;
  94. if (!OpenAndReadSettings(&settings))
  95. return false;
  96. *time = InRangeCast<time_t>(settings.last_upload_attempt_time,
  97. std::numeric_limits<time_t>::max());
  98. return true;
  99. }
  100. bool Settings::SetLastUploadAttemptTime(time_t time) {
  101. DCHECK(initialized_.is_valid());
  102. Data settings;
  103. ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
  104. if (!handle.is_valid())
  105. return false;
  106. settings.last_upload_attempt_time = InRangeCast<int64_t>(time, 0);
  107. return WriteSettings(handle.get(), settings);
  108. }
  109. // static
  110. Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
  111. FileHandle file,
  112. FileLocking locking) {
  113. ScopedFileHandle scoped(file);
  114. if (scoped.is_valid()) {
  115. if (!LoggingLockFile(scoped.get(), locking))
  116. scoped.reset();
  117. }
  118. return ScopedLockedFileHandle(scoped.release());
  119. }
  120. Settings::ScopedLockedFileHandle Settings::OpenForReading() {
  121. return MakeScopedLockedFileHandle(LoggingOpenFileForRead(file_path()),
  122. FileLocking::kShared);
  123. }
  124. Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting(
  125. FileWriteMode mode, bool log_open_error) {
  126. DCHECK(mode != FileWriteMode::kTruncateOrCreate);
  127. FileHandle handle;
  128. if (log_open_error) {
  129. handle = LoggingOpenFileForReadAndWrite(
  130. file_path(), mode, FilePermissions::kWorldReadable);
  131. } else {
  132. handle = OpenFileForReadAndWrite(
  133. file_path(), mode, FilePermissions::kWorldReadable);
  134. }
  135. return MakeScopedLockedFileHandle(handle, FileLocking::kExclusive);
  136. }
  137. bool Settings::OpenAndReadSettings(Data* out_data) {
  138. ScopedLockedFileHandle handle = OpenForReading();
  139. if (!handle.is_valid())
  140. return false;
  141. if (ReadSettings(handle.get(), out_data, true))
  142. return true;
  143. // The settings file is corrupt, so reinitialize it.
  144. handle.reset();
  145. // The settings failed to be read, so re-initialize them.
  146. return RecoverSettings(kInvalidFileHandle, out_data);
  147. }
  148. Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings(
  149. Data* out_data) {
  150. ScopedLockedFileHandle handle;
  151. bool created = false;
  152. if (!initialized_.is_valid()) {
  153. // If this object is initializing, it hasn’t seen a settings file already,
  154. // so go easy on errors. Creating a new settings file for the first time
  155. // shouldn’t spew log messages.
  156. //
  157. // First, try to use an existing settings file.
  158. handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrFail, false);
  159. if (!handle.is_valid()) {
  160. // Create a new settings file if it didn’t already exist.
  161. handle = OpenForReadingAndWriting(FileWriteMode::kCreateOrFail, false);
  162. if (handle.is_valid()) {
  163. created = true;
  164. }
  165. // There may have been a race to create the file, and something else may
  166. // have won. There will be one more attempt to try to open or create the
  167. // file below.
  168. }
  169. }
  170. if (!handle.is_valid()) {
  171. // Either the object is initialized, meaning it’s already seen a valid
  172. // settings file, or the object is initializing and none of the above
  173. // attempts to create the settings file succeeded. Either way, this is the
  174. // last chance for success, so if this fails, log a message.
  175. handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);
  176. }
  177. if (!handle.is_valid())
  178. return ScopedLockedFileHandle();
  179. // Attempt reading the settings even if the file is known to have just been
  180. // created. The file-create and file-lock operations don’t occur atomically,
  181. // and something else may have written the settings before this invocation
  182. // took the lock. If the settings file was definitely just created, though,
  183. // don’t log any read errors. The expected non-race behavior in this case is a
  184. // zero-length read, with ReadSettings() failing.
  185. if (!ReadSettings(handle.get(), out_data, !created)) {
  186. if (!RecoverSettings(handle.get(), out_data))
  187. return ScopedLockedFileHandle();
  188. }
  189. return handle;
  190. }
  191. bool Settings::ReadSettings(FileHandle handle,
  192. Data* out_data,
  193. bool log_read_error) {
  194. if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
  195. return false;
  196. bool read_result =
  197. log_read_error
  198. ? LoggingReadFileExactly(handle, out_data, sizeof(*out_data))
  199. : ReadFileExactly(handle, out_data, sizeof(*out_data));
  200. if (!read_result)
  201. return false;
  202. if (out_data->magic != Data::kSettingsMagic) {
  203. LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic;
  204. return false;
  205. }
  206. if (out_data->version != Data::kSettingsVersion) {
  207. LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion;
  208. return false;
  209. }
  210. return true;
  211. }
  212. bool Settings::WriteSettings(FileHandle handle, const Data& data) {
  213. if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
  214. return false;
  215. if (!LoggingTruncateFile(handle))
  216. return false;
  217. return LoggingWriteFile(handle, &data, sizeof(Data));
  218. }
  219. bool Settings::RecoverSettings(FileHandle handle, Data* out_data) {
  220. ScopedLockedFileHandle scoped_handle;
  221. if (handle == kInvalidFileHandle) {
  222. scoped_handle =
  223. OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);
  224. handle = scoped_handle.get();
  225. // Test if the file has already been recovered now that the exclusive lock
  226. // is held.
  227. if (ReadSettings(handle, out_data, true))
  228. return true;
  229. }
  230. if (handle == kInvalidFileHandle) {
  231. LOG(ERROR) << "Invalid file handle";
  232. return false;
  233. }
  234. if (!InitializeSettings(handle))
  235. return false;
  236. return ReadSettings(handle, out_data, true);
  237. }
  238. bool Settings::InitializeSettings(FileHandle handle) {
  239. Data settings;
  240. if (!settings.client_id.InitializeWithNew())
  241. return false;
  242. return WriteSettings(handle, settings);
  243. }
  244. } // namespace crashpad