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