• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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