1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/prefs/json_pref_store.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <string>
11 #include <utility>
12
13 #include "base/feature_list.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/functional/callback_helpers.h"
19 #include "base/json/json_file_value_serializer.h"
20 #include "base/json/json_writer.h"
21 #include "base/logging.h"
22 #include "base/memory/ref_counted.h"
23 #include "base/metrics/histogram.h"
24 #include "base/notreached.h"
25 #include "base/observer_list.h"
26 #include "base/ranges/algorithm.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_piece.h"
29 #include "base/strings/string_util.h"
30 #include "base/task/sequenced_task_runner.h"
31 #include "base/time/default_clock.h"
32 #include "base/values.h"
33 #include "components/prefs/pref_filter.h"
34
35 // Result returned from internal read tasks.
36 struct JsonPrefStore::ReadResult {
37 public:
38 ReadResult();
39 ~ReadResult();
40 ReadResult(const ReadResult&) = delete;
41 ReadResult& operator=(const ReadResult&) = delete;
42
43 std::unique_ptr<base::Value> value;
44 PrefReadError error = PersistentPrefStore::PREF_READ_ERROR_NONE;
45 bool no_dir = false;
46 size_t num_bytes_read = 0u;
47 };
48
49 JsonPrefStore::ReadResult::ReadResult() = default;
50 JsonPrefStore::ReadResult::~ReadResult() = default;
51
52 namespace {
53
54 // Some extensions we'll tack on to copies of the Preferences files.
55 const base::FilePath::CharType kBadExtension[] = FILE_PATH_LITERAL("bad");
56
BackupPrefsFile(const base::FilePath & path)57 bool BackupPrefsFile(const base::FilePath& path) {
58 const base::FilePath bad = path.ReplaceExtension(kBadExtension);
59 const bool bad_existed = base::PathExists(bad);
60 base::Move(path, bad);
61 return bad_existed;
62 }
63
HandleReadErrors(const base::Value * value,const base::FilePath & path,int error_code,const std::string & error_msg)64 PersistentPrefStore::PrefReadError HandleReadErrors(
65 const base::Value* value,
66 const base::FilePath& path,
67 int error_code,
68 const std::string& error_msg) {
69 if (!value) {
70 DVLOG(1) << "Error while loading JSON file: " << error_msg
71 << ", file: " << path.value();
72 switch (error_code) {
73 case JSONFileValueDeserializer::JSON_ACCESS_DENIED:
74 return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
75 case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE:
76 return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
77 case JSONFileValueDeserializer::JSON_FILE_LOCKED:
78 return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
79 case JSONFileValueDeserializer::JSON_NO_SUCH_FILE:
80 return PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
81 default:
82 // JSON errors indicate file corruption of some sort.
83 // Since the file is corrupt, move it to the side and continue with
84 // empty preferences. This will result in them losing their settings.
85 // We keep the old file for possible support and debugging assistance
86 // as well as to detect if they're seeing these errors repeatedly.
87 // TODO(erikkay) Instead, use the last known good file.
88 // If they've ever had a parse error before, put them in another bucket.
89 // TODO(erikkay) if we keep this error checking for very long, we may
90 // want to differentiate between recent and long ago errors.
91 const bool bad_existed = BackupPrefsFile(path);
92 return bad_existed ? PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT
93 : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
94 }
95 }
96 if (!value->is_dict())
97 return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
98 return PersistentPrefStore::PREF_READ_ERROR_NONE;
99 }
100
ReadPrefsFromDisk(const base::FilePath & path)101 std::unique_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk(
102 const base::FilePath& path) {
103 int error_code;
104 std::string error_msg;
105 auto read_result = std::make_unique<JsonPrefStore::ReadResult>();
106 JSONFileValueDeserializer deserializer(path);
107 read_result->value = deserializer.Deserialize(&error_code, &error_msg);
108 read_result->error =
109 HandleReadErrors(read_result->value.get(), path, error_code, error_msg);
110 read_result->no_dir = !base::PathExists(path.DirName());
111 read_result->num_bytes_read = deserializer.get_last_read_size();
112
113 return read_result;
114 }
115
116 // Returns the a histogram suffix for a few allowlisted JsonPref files.
GetHistogramSuffix(const base::FilePath & path)117 const char* GetHistogramSuffix(const base::FilePath& path) {
118 std::string spaceless_basename;
119 base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_",
120 &spaceless_basename);
121 static constexpr std::array<const char*, 3> kAllowList{
122 "Secure_Preferences", "Preferences", "Local_State"};
123 const char* const* it = base::ranges::find(kAllowList, spaceless_basename);
124 return it != kAllowList.end() ? *it : "";
125 }
126
DoSerialize(base::ValueView value,const base::FilePath & path)127 absl::optional<std::string> DoSerialize(base::ValueView value,
128 const base::FilePath& path) {
129 std::string output;
130 if (!base::JSONWriter::Write(value, &output)) {
131 // Failed to serialize prefs file. Backup the existing prefs file and
132 // crash.
133 BackupPrefsFile(path);
134 NOTREACHED_NORETURN() << "Failed to serialize preferences : " << path
135 << "\nBacked up under "
136 << path.ReplaceExtension(kBadExtension);
137 }
138 return output;
139 }
140
141 } // namespace
142
JsonPrefStore(const base::FilePath & pref_filename,std::unique_ptr<PrefFilter> pref_filter,scoped_refptr<base::SequencedTaskRunner> file_task_runner,bool read_only)143 JsonPrefStore::JsonPrefStore(
144 const base::FilePath& pref_filename,
145 std::unique_ptr<PrefFilter> pref_filter,
146 scoped_refptr<base::SequencedTaskRunner> file_task_runner,
147 bool read_only)
148 : path_(pref_filename),
149 file_task_runner_(std::move(file_task_runner)),
150 read_only_(read_only),
151 writer_(pref_filename,
152 file_task_runner_,
153 GetHistogramSuffix(pref_filename)),
154 pref_filter_(std::move(pref_filter)),
155 initialized_(false),
156 filtering_in_progress_(false),
157 pending_lossy_write_(false),
158 read_error_(PREF_READ_ERROR_NONE),
159 has_pending_write_reply_(false) {
160 DCHECK(!path_.empty());
161 }
162
GetValue(base::StringPiece key,const base::Value ** result) const163 bool JsonPrefStore::GetValue(base::StringPiece key,
164 const base::Value** result) const {
165 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
166
167 const base::Value* tmp = prefs_.FindByDottedPath(key);
168 if (!tmp)
169 return false;
170
171 if (result)
172 *result = tmp;
173 return true;
174 }
175
GetValues() const176 base::Value::Dict JsonPrefStore::GetValues() const {
177 return prefs_.Clone();
178 }
179
AddObserver(PrefStore::Observer * observer)180 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
181 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
182
183 observers_.AddObserver(observer);
184 }
185
RemoveObserver(PrefStore::Observer * observer)186 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
187 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
188
189 observers_.RemoveObserver(observer);
190 }
191
HasObservers() const192 bool JsonPrefStore::HasObservers() const {
193 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
194 return !observers_.empty();
195 }
196
IsInitializationComplete() const197 bool JsonPrefStore::IsInitializationComplete() const {
198 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
199
200 return initialized_;
201 }
202
GetMutableValue(const std::string & key,base::Value ** result)203 bool JsonPrefStore::GetMutableValue(const std::string& key,
204 base::Value** result) {
205 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
206
207 base::Value* tmp = prefs_.FindByDottedPath(key);
208 if (!tmp)
209 return false;
210
211 if (result)
212 *result = tmp;
213 return true;
214 }
215
SetValue(const std::string & key,base::Value value,uint32_t flags)216 void JsonPrefStore::SetValue(const std::string& key,
217 base::Value value,
218 uint32_t flags) {
219 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
220
221 base::Value* old_value = prefs_.FindByDottedPath(key);
222 if (!old_value || value != *old_value) {
223 prefs_.SetByDottedPath(key, std::move(value));
224 ReportValueChanged(key, flags);
225 }
226 }
227
SetValueSilently(const std::string & key,base::Value value,uint32_t flags)228 void JsonPrefStore::SetValueSilently(const std::string& key,
229 base::Value value,
230 uint32_t flags) {
231 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
232
233 base::Value* old_value = prefs_.FindByDottedPath(key);
234 if (!old_value || value != *old_value) {
235 prefs_.SetByDottedPath(key, std::move(value));
236 ScheduleWrite(flags);
237 }
238 }
239
RemoveValue(const std::string & key,uint32_t flags)240 void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
241 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
242
243 if (prefs_.RemoveByDottedPath(key)) {
244 ReportValueChanged(key, flags);
245 }
246 }
247
RemoveValueSilently(const std::string & key,uint32_t flags)248 void JsonPrefStore::RemoveValueSilently(const std::string& key,
249 uint32_t flags) {
250 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
251
252 prefs_.RemoveByDottedPath(key);
253 ScheduleWrite(flags);
254 }
255
RemoveValuesByPrefixSilently(const std::string & prefix)256 void JsonPrefStore::RemoveValuesByPrefixSilently(const std::string& prefix) {
257 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
258 RemoveValueSilently(prefix, /*flags*/ 0);
259 }
260
ReadOnly() const261 bool JsonPrefStore::ReadOnly() const {
262 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
263
264 return read_only_;
265 }
266
GetReadError() const267 PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
268 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
269
270 return read_error_;
271 }
272
ReadPrefs()273 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
274 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
275
276 OnFileRead(ReadPrefsFromDisk(path_));
277 return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
278 : read_error_;
279 }
280
ReadPrefsAsync(ReadErrorDelegate * error_delegate)281 void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
282 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
283
284 initialized_ = false;
285 error_delegate_.reset(error_delegate);
286
287 // Weakly binds the read task so that it doesn't kick in during shutdown.
288 file_task_runner_->PostTaskAndReplyWithResult(
289 FROM_HERE, base::BindOnce(&ReadPrefsFromDisk, path_),
290 base::BindOnce(&JsonPrefStore::OnFileRead, AsWeakPtr()));
291 }
292
CommitPendingWrite(base::OnceClosure reply_callback,base::OnceClosure synchronous_done_callback)293 void JsonPrefStore::CommitPendingWrite(
294 base::OnceClosure reply_callback,
295 base::OnceClosure synchronous_done_callback) {
296 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
297
298 // Schedule a write for any lossy writes that are outstanding to ensure that
299 // they get flushed when this function is called.
300 SchedulePendingLossyWrites();
301
302 if (writer_.HasPendingWrite() && !read_only_)
303 writer_.DoScheduledWrite();
304
305 // Since disk operations occur on |file_task_runner_|, the reply of a task
306 // posted to |file_task_runner_| will run after currently pending disk
307 // operations. Also, by definition of PostTaskAndReply(), the reply (in the
308 // |reply_callback| case will run on the current sequence.
309
310 if (synchronous_done_callback) {
311 file_task_runner_->PostTask(FROM_HERE,
312 std::move(synchronous_done_callback));
313 }
314
315 if (reply_callback) {
316 file_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
317 std::move(reply_callback));
318 }
319 }
320
SchedulePendingLossyWrites()321 void JsonPrefStore::SchedulePendingLossyWrites() {
322 if (pending_lossy_write_)
323 writer_.ScheduleWrite(this);
324 }
325
ReportValueChanged(const std::string & key,uint32_t flags)326 void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
327 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
328
329 if (pref_filter_)
330 pref_filter_->FilterUpdate(key);
331
332 for (PrefStore::Observer& observer : observers_)
333 observer.OnPrefValueChanged(key);
334
335 ScheduleWrite(flags);
336 }
337
PerformPreserializationTasks()338 void JsonPrefStore::PerformPreserializationTasks() {
339 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
340 pending_lossy_write_ = false;
341 if (pref_filter_) {
342 OnWriteCallbackPair callbacks = pref_filter_->FilterSerializeData(prefs_);
343 if (!callbacks.first.is_null() || !callbacks.second.is_null())
344 RegisterOnNextWriteSynchronousCallbacks(std::move(callbacks));
345 }
346 }
347
RunOrScheduleNextSuccessfulWriteCallback(bool write_success)348 void JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback(
349 bool write_success) {
350 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
351
352 has_pending_write_reply_ = false;
353 if (!on_next_successful_write_reply_.is_null()) {
354 base::OnceClosure on_successful_write =
355 std::move(on_next_successful_write_reply_);
356 if (write_success) {
357 std::move(on_successful_write).Run();
358 } else {
359 RegisterOnNextSuccessfulWriteReply(std::move(on_successful_write));
360 }
361 }
362 }
363
364 // static
PostWriteCallback(base::OnceCallback<void (bool success)> on_next_write_callback,base::OnceCallback<void (bool success)> on_next_write_reply,scoped_refptr<base::SequencedTaskRunner> reply_task_runner,bool write_success)365 void JsonPrefStore::PostWriteCallback(
366 base::OnceCallback<void(bool success)> on_next_write_callback,
367 base::OnceCallback<void(bool success)> on_next_write_reply,
368 scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
369 bool write_success) {
370 if (!on_next_write_callback.is_null())
371 std::move(on_next_write_callback).Run(write_success);
372
373 // We can't run |on_next_write_reply| on the current thread. Bounce back to
374 // the |reply_task_runner| which is the correct sequenced thread.
375 reply_task_runner->PostTask(
376 FROM_HERE, base::BindOnce(std::move(on_next_write_reply), write_success));
377 }
378
RegisterOnNextSuccessfulWriteReply(base::OnceClosure on_next_successful_write_reply)379 void JsonPrefStore::RegisterOnNextSuccessfulWriteReply(
380 base::OnceClosure on_next_successful_write_reply) {
381 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
382 DCHECK(on_next_successful_write_reply_.is_null());
383
384 on_next_successful_write_reply_ = std::move(on_next_successful_write_reply);
385
386 // If there are pending callbacks, avoid erasing them; the reply will be used
387 // as we set |on_next_successful_write_reply_|. Otherwise, setup a reply with
388 // an empty callback.
389 if (!has_pending_write_reply_) {
390 has_pending_write_reply_ = true;
391 writer_.RegisterOnNextWriteCallbacks(
392 base::OnceClosure(),
393 base::BindOnce(
394 &PostWriteCallback, base::OnceCallback<void(bool success)>(),
395 base::BindOnce(
396 &JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
397 AsWeakPtr()),
398 base::SequencedTaskRunner::GetCurrentDefault()));
399 }
400 }
401
RegisterOnNextWriteSynchronousCallbacks(OnWriteCallbackPair callbacks)402 void JsonPrefStore::RegisterOnNextWriteSynchronousCallbacks(
403 OnWriteCallbackPair callbacks) {
404 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
405
406 has_pending_write_reply_ = true;
407
408 writer_.RegisterOnNextWriteCallbacks(
409 std::move(callbacks.first),
410 base::BindOnce(
411 &PostWriteCallback, std::move(callbacks.second),
412 base::BindOnce(
413 &JsonPrefStore::RunOrScheduleNextSuccessfulWriteCallback,
414 AsWeakPtr()),
415 base::SequencedTaskRunner::GetCurrentDefault()));
416 }
417
OnStoreDeletionFromDisk()418 void JsonPrefStore::OnStoreDeletionFromDisk() {
419 if (pref_filter_)
420 pref_filter_->OnStoreDeletionFromDisk();
421 }
422
OnFileRead(std::unique_ptr<ReadResult> read_result)423 void JsonPrefStore::OnFileRead(std::unique_ptr<ReadResult> read_result) {
424 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
425
426 DCHECK(read_result);
427
428 base::Value::Dict unfiltered_prefs;
429
430 read_error_ = read_result->error;
431
432 bool initialization_successful = !read_result->no_dir;
433
434 if (initialization_successful) {
435 switch (read_error_) {
436 case PREF_READ_ERROR_ACCESS_DENIED:
437 case PREF_READ_ERROR_FILE_OTHER:
438 case PREF_READ_ERROR_FILE_LOCKED:
439 case PREF_READ_ERROR_JSON_TYPE:
440 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
441 read_only_ = true;
442 break;
443 case PREF_READ_ERROR_NONE:
444 DCHECK(read_result->value);
445 DCHECK(read_result->value->is_dict());
446 writer_.set_previous_data_size(read_result->num_bytes_read);
447 unfiltered_prefs = std::move(*read_result->value).TakeDict();
448 break;
449 case PREF_READ_ERROR_NO_FILE:
450 // If the file just doesn't exist, maybe this is first run. In any case
451 // there's no harm in writing out default prefs in this case.
452 case PREF_READ_ERROR_JSON_PARSE:
453 case PREF_READ_ERROR_JSON_REPEAT:
454 break;
455 case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
456 // This is a special error code to be returned by ReadPrefs when it
457 // can't complete synchronously, it should never be returned by the read
458 // operation itself.
459 case PREF_READ_ERROR_MAX_ENUM:
460 NOTREACHED();
461 break;
462 }
463 }
464
465 if (pref_filter_) {
466 filtering_in_progress_ = true;
467 PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
468 base::BindOnce(&JsonPrefStore::FinalizeFileRead, AsWeakPtr(),
469 initialization_successful));
470 pref_filter_->FilterOnLoad(std::move(post_filter_on_load_callback),
471 std::move(unfiltered_prefs));
472 } else {
473 FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs),
474 false);
475 }
476 }
477
~JsonPrefStore()478 JsonPrefStore::~JsonPrefStore() {
479 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
480 CommitPendingWrite();
481 }
482
SerializeData()483 absl::optional<std::string> JsonPrefStore::SerializeData() {
484 PerformPreserializationTasks();
485 return DoSerialize(prefs_, path_);
486 }
487
488 base::ImportantFileWriter::BackgroundDataProducerCallback
GetSerializedDataProducerForBackgroundSequence()489 JsonPrefStore::GetSerializedDataProducerForBackgroundSequence() {
490 PerformPreserializationTasks();
491 return base::BindOnce(&DoSerialize, prefs_.Clone(), path_);
492 }
493
FinalizeFileRead(bool initialization_successful,base::Value::Dict prefs,bool schedule_write)494 void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
495 base::Value::Dict prefs,
496 bool schedule_write) {
497 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
498
499 filtering_in_progress_ = false;
500
501 if (!initialization_successful) {
502 for (PrefStore::Observer& observer : observers_)
503 observer.OnInitializationCompleted(false);
504 return;
505 }
506
507 prefs_ = std::move(prefs);
508
509 initialized_ = true;
510
511 if (schedule_write)
512 ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
513
514 if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
515 error_delegate_->OnError(read_error_);
516
517 for (PrefStore::Observer& observer : observers_)
518 observer.OnInitializationCompleted(true);
519
520 return;
521 }
522
ScheduleWrite(uint32_t flags)523 void JsonPrefStore::ScheduleWrite(uint32_t flags) {
524 if (read_only_)
525 return;
526
527 if (flags & LOSSY_PREF_WRITE_FLAG) {
528 pending_lossy_write_ = true;
529 } else {
530 writer_.ScheduleWriteWithBackgroundDataSerializer(this);
531 }
532 }
533