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