• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
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 "chrome/browser/prefs/tracked/tracked_preferences_migration.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/macros.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/metrics/histogram.h"
12 #include "base/values.h"
13 #include "chrome/browser/prefs/interceptable_pref_filter.h"
14 #include "chrome/browser/prefs/pref_hash_store.h"
15 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
16 #include "chrome/browser/prefs/tracked/dictionary_hash_store_contents.h"
17 #include "chrome/browser/prefs/tracked/hash_store_contents.h"
18 
19 namespace {
20 
21 class TrackedPreferencesMigrator
22     : public base::RefCounted<TrackedPreferencesMigrator> {
23  public:
24   TrackedPreferencesMigrator(
25       const std::set<std::string>& unprotected_pref_names,
26       const std::set<std::string>& protected_pref_names,
27       const base::Callback<void(const std::string& key)>&
28           unprotected_store_cleaner,
29       const base::Callback<void(const std::string& key)>&
30           protected_store_cleaner,
31       const base::Callback<void(const base::Closure&)>&
32           register_on_successful_unprotected_store_write_callback,
33       const base::Callback<void(const base::Closure&)>&
34           register_on_successful_protected_store_write_callback,
35       scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
36       scoped_ptr<PrefHashStore> protected_pref_hash_store,
37       scoped_ptr<HashStoreContents> legacy_pref_hash_store,
38       InterceptablePrefFilter* unprotected_pref_filter,
39       InterceptablePrefFilter* protected_pref_filter);
40 
41  private:
42   friend class base::RefCounted<TrackedPreferencesMigrator>;
43 
44   enum PrefFilterID {
45     UNPROTECTED_PREF_FILTER,
46     PROTECTED_PREF_FILTER
47   };
48 
49   ~TrackedPreferencesMigrator();
50 
51   // Stores the data coming in from the filter identified by |id| into this
52   // class and then calls MigrateIfReady();
53   void InterceptFilterOnLoad(
54       PrefFilterID id,
55       const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
56           finalize_filter_on_load,
57       scoped_ptr<base::DictionaryValue> prefs);
58 
59   // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_|
60   // have been set.
61   void MigrateIfReady();
62 
63   const std::set<std::string> unprotected_pref_names_;
64   const std::set<std::string> protected_pref_names_;
65 
66   const base::Callback<void(const std::string& key)> unprotected_store_cleaner_;
67   const base::Callback<void(const std::string& key)> protected_store_cleaner_;
68   const base::Callback<void(const base::Closure&)>
69       register_on_successful_unprotected_store_write_callback_;
70   const base::Callback<void(const base::Closure&)>
71       register_on_successful_protected_store_write_callback_;
72 
73   InterceptablePrefFilter::FinalizeFilterOnLoadCallback
74       finalize_unprotected_filter_on_load_;
75   InterceptablePrefFilter::FinalizeFilterOnLoadCallback
76       finalize_protected_filter_on_load_;
77 
78   scoped_ptr<PrefHashStore> unprotected_pref_hash_store_;
79   scoped_ptr<PrefHashStore> protected_pref_hash_store_;
80   scoped_ptr<HashStoreContents> legacy_pref_hash_store_;
81 
82   scoped_ptr<base::DictionaryValue> unprotected_prefs_;
83   scoped_ptr<base::DictionaryValue> protected_prefs_;
84 
85   DISALLOW_COPY_AND_ASSIGN(TrackedPreferencesMigrator);
86 };
87 
88 // Invokes |store_cleaner| for every |keys_to_clean|.
CleanupPrefStore(const base::Callback<void (const std::string & key)> & store_cleaner,const std::set<std::string> & keys_to_clean)89 void CleanupPrefStore(
90     const base::Callback<void(const std::string& key)>& store_cleaner,
91     const std::set<std::string>& keys_to_clean) {
92   for (std::set<std::string>::const_iterator it = keys_to_clean.begin();
93        it != keys_to_clean.end(); ++it) {
94     store_cleaner.Run(*it);
95   }
96 }
97 
98 // If |wait_for_commit_to_destination_store|: schedules (via
99 // |register_on_successful_destination_store_write_callback|) a cleanup of the
100 // |keys_to_clean| from the source pref store (through |source_store_cleaner|)
101 // once the destination pref store they were migrated to was successfully
102 // written to disk. Otherwise, executes the cleanup right away.
ScheduleSourcePrefStoreCleanup(const base::Callback<void (const base::Closure &)> & register_on_successful_destination_store_write_callback,const base::Callback<void (const std::string & key)> & source_store_cleaner,const std::set<std::string> & keys_to_clean,bool wait_for_commit_to_destination_store)103 void ScheduleSourcePrefStoreCleanup(
104     const base::Callback<void(const base::Closure&)>&
105         register_on_successful_destination_store_write_callback,
106     const base::Callback<void(const std::string& key)>& source_store_cleaner,
107     const std::set<std::string>& keys_to_clean,
108     bool wait_for_commit_to_destination_store) {
109   DCHECK(!keys_to_clean.empty());
110   if (wait_for_commit_to_destination_store) {
111     register_on_successful_destination_store_write_callback.Run(
112         base::Bind(&CleanupPrefStore, source_store_cleaner, keys_to_clean));
113   } else {
114     CleanupPrefStore(source_store_cleaner, keys_to_clean);
115   }
116 }
117 
118 // Removes hashes for |migrated_pref_names| from |origin_pref_store| using
119 // the configuration/implementation in |origin_pref_hash_store|.
CleanupMigratedHashes(const std::set<std::string> & migrated_pref_names,PrefHashStore * origin_pref_hash_store,base::DictionaryValue * origin_pref_store)120 void CleanupMigratedHashes(const std::set<std::string>& migrated_pref_names,
121                            PrefHashStore* origin_pref_hash_store,
122                            base::DictionaryValue* origin_pref_store) {
123   scoped_ptr<PrefHashStoreTransaction> transaction(
124       origin_pref_hash_store->BeginTransaction(scoped_ptr<HashStoreContents>(
125           new DictionaryHashStoreContents(origin_pref_store))));
126   for (std::set<std::string>::const_iterator it = migrated_pref_names.begin();
127        it != migrated_pref_names.end();
128        ++it) {
129     transaction->ClearHash(*it);
130   }
131 }
132 
133 // Copies the value of each pref in |pref_names| which is set in |old_store|,
134 // but not in |new_store| into |new_store|. Sets |old_store_needs_cleanup| to
135 // true if any old duplicates remain in |old_store| and sets |new_store_altered|
136 // to true if any value was copied to |new_store|.
MigratePrefsFromOldToNewStore(const std::set<std::string> & pref_names,base::DictionaryValue * old_store,base::DictionaryValue * new_store,PrefHashStore * new_hash_store,HashStoreContents * legacy_hash_store,bool * old_store_needs_cleanup,bool * new_store_altered,bool * used_legacy_pref_hashes)137 void MigratePrefsFromOldToNewStore(const std::set<std::string>& pref_names,
138                                    base::DictionaryValue* old_store,
139                                    base::DictionaryValue* new_store,
140                                    PrefHashStore* new_hash_store,
141                                    HashStoreContents* legacy_hash_store,
142                                    bool* old_store_needs_cleanup,
143                                    bool* new_store_altered,
144                                    bool* used_legacy_pref_hashes) {
145   const base::DictionaryValue* old_hash_store_contents =
146       DictionaryHashStoreContents(old_store).GetContents();
147   const base::DictionaryValue* legacy_hash_store_contents =
148       legacy_hash_store->GetContents();
149   scoped_ptr<PrefHashStoreTransaction> new_hash_store_transaction(
150       new_hash_store->BeginTransaction(scoped_ptr<HashStoreContents>(
151           new DictionaryHashStoreContents(new_store))));
152 
153   for (std::set<std::string>::const_iterator it = pref_names.begin();
154        it != pref_names.end();
155        ++it) {
156      const std::string& pref_name = *it;
157      const base::Value* value_in_old_store = NULL;
158 
159     // If the destination does not have a hash for this pref we will
160     // unconditionally attempt to move it.
161     bool destination_hash_missing =
162         !new_hash_store_transaction->HasHash(pref_name);
163     // If we migrate the value we will also attempt to migrate the hash.
164     bool migrated_value = false;
165     if (old_store->Get(pref_name, &value_in_old_store)) {
166       // Whether this value ends up being copied below or was left behind by a
167       // previous incomplete migration, it should be cleaned up.
168       *old_store_needs_cleanup = true;
169 
170       if (!new_store->Get(pref_name, NULL)) {
171         // Copy the value from |old_store| to |new_store| rather than moving it
172         // to avoid data loss should |old_store| be flushed to disk without
173         // |new_store| having equivalently been successfully flushed to disk
174         // (e.g., on crash or in cases where |new_store| is read-only following
175         // a read error on startup).
176         new_store->Set(pref_name, value_in_old_store->DeepCopy());
177         migrated_value = true;
178         *new_store_altered = true;
179       }
180     }
181 
182     if (destination_hash_missing || migrated_value) {
183       const base::Value* old_hash = NULL;
184       if (old_hash_store_contents)
185         old_hash_store_contents->Get(pref_name, &old_hash);
186       if (!old_hash && legacy_hash_store_contents) {
187         legacy_hash_store_contents->Get(pref_name, &old_hash);
188         if (old_hash)
189           *used_legacy_pref_hashes = true;
190       }
191       if (old_hash) {
192         new_hash_store_transaction->ImportHash(pref_name, old_hash);
193         *new_store_altered = true;
194       } else if (!destination_hash_missing) {
195         // Do not allow values to be migrated without MACs if the destination
196         // already has a MAC (http://crbug.com/414554). Remove the migrated
197         // value in order to provide the same no-op behaviour as if the pref was
198         // added to the wrong file when there was already a value for
199         // |pref_name| in |new_store|.
200         new_store->Remove(pref_name, NULL);
201         *new_store_altered = true;
202       }
203     }
204   }
205 }
206 
TrackedPreferencesMigrator(const std::set<std::string> & unprotected_pref_names,const std::set<std::string> & protected_pref_names,const base::Callback<void (const std::string & key)> & unprotected_store_cleaner,const base::Callback<void (const std::string & key)> & protected_store_cleaner,const base::Callback<void (const base::Closure &)> & register_on_successful_unprotected_store_write_callback,const base::Callback<void (const base::Closure &)> & register_on_successful_protected_store_write_callback,scoped_ptr<PrefHashStore> unprotected_pref_hash_store,scoped_ptr<PrefHashStore> protected_pref_hash_store,scoped_ptr<HashStoreContents> legacy_pref_hash_store,InterceptablePrefFilter * unprotected_pref_filter,InterceptablePrefFilter * protected_pref_filter)207 TrackedPreferencesMigrator::TrackedPreferencesMigrator(
208     const std::set<std::string>& unprotected_pref_names,
209     const std::set<std::string>& protected_pref_names,
210     const base::Callback<void(const std::string& key)>&
211         unprotected_store_cleaner,
212     const base::Callback<void(const std::string& key)>& protected_store_cleaner,
213     const base::Callback<void(const base::Closure&)>&
214         register_on_successful_unprotected_store_write_callback,
215     const base::Callback<void(const base::Closure&)>&
216         register_on_successful_protected_store_write_callback,
217     scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
218     scoped_ptr<PrefHashStore> protected_pref_hash_store,
219     scoped_ptr<HashStoreContents> legacy_pref_hash_store,
220     InterceptablePrefFilter* unprotected_pref_filter,
221     InterceptablePrefFilter* protected_pref_filter)
222     : unprotected_pref_names_(unprotected_pref_names),
223       protected_pref_names_(protected_pref_names),
224       unprotected_store_cleaner_(unprotected_store_cleaner),
225       protected_store_cleaner_(protected_store_cleaner),
226       register_on_successful_unprotected_store_write_callback_(
227           register_on_successful_unprotected_store_write_callback),
228       register_on_successful_protected_store_write_callback_(
229           register_on_successful_protected_store_write_callback),
230       unprotected_pref_hash_store_(unprotected_pref_hash_store.Pass()),
231       protected_pref_hash_store_(protected_pref_hash_store.Pass()),
232       legacy_pref_hash_store_(legacy_pref_hash_store.Pass()) {
233   // The callbacks bound below will own this TrackedPreferencesMigrator by
234   // reference.
235   unprotected_pref_filter->InterceptNextFilterOnLoad(
236       base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
237                  this,
238                  UNPROTECTED_PREF_FILTER));
239   protected_pref_filter->InterceptNextFilterOnLoad(
240       base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
241                  this,
242                  PROTECTED_PREF_FILTER));
243 }
244 
~TrackedPreferencesMigrator()245 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
246 
InterceptFilterOnLoad(PrefFilterID id,const InterceptablePrefFilter::FinalizeFilterOnLoadCallback & finalize_filter_on_load,scoped_ptr<base::DictionaryValue> prefs)247 void TrackedPreferencesMigrator::InterceptFilterOnLoad(
248     PrefFilterID id,
249     const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
250         finalize_filter_on_load,
251     scoped_ptr<base::DictionaryValue> prefs) {
252   switch (id) {
253     case UNPROTECTED_PREF_FILTER:
254       finalize_unprotected_filter_on_load_ = finalize_filter_on_load;
255       unprotected_prefs_ = prefs.Pass();
256       break;
257     case PROTECTED_PREF_FILTER:
258       finalize_protected_filter_on_load_ = finalize_filter_on_load;
259       protected_prefs_ = prefs.Pass();
260       break;
261   }
262 
263   MigrateIfReady();
264 }
265 
MigrateIfReady()266 void TrackedPreferencesMigrator::MigrateIfReady() {
267   // Wait for both stores to have been read before proceeding.
268   if (!protected_prefs_ || !unprotected_prefs_)
269     return;
270 
271   bool used_legacy_pref_hashes = false;
272   bool protected_prefs_need_cleanup = false;
273   bool unprotected_prefs_altered = false;
274   MigratePrefsFromOldToNewStore(unprotected_pref_names_,
275                                 protected_prefs_.get(),
276                                 unprotected_prefs_.get(),
277                                 unprotected_pref_hash_store_.get(),
278                                 legacy_pref_hash_store_.get(),
279                                 &protected_prefs_need_cleanup,
280                                 &unprotected_prefs_altered,
281                                 &used_legacy_pref_hashes);
282   bool unprotected_prefs_need_cleanup = false;
283   bool protected_prefs_altered = false;
284   MigratePrefsFromOldToNewStore(protected_pref_names_,
285                                 unprotected_prefs_.get(),
286                                 protected_prefs_.get(),
287                                 protected_pref_hash_store_.get(),
288                                 legacy_pref_hash_store_.get(),
289                                 &unprotected_prefs_need_cleanup,
290                                 &protected_prefs_altered,
291                                 &used_legacy_pref_hashes);
292   UMA_HISTOGRAM_BOOLEAN("Settings.MigratedHashesFromLocalState",
293                         used_legacy_pref_hashes);
294 
295   if (!unprotected_prefs_altered && !protected_prefs_altered) {
296     // Clean up any MACs that might have been previously migrated from the
297     // various stores. It's safe to leave them behind for a little while as they
298     // will be ignored unless the corresponding value is _also_ present. The
299     // cleanup must be deferred until the MACs have been written to their target
300     // stores, and doing so in a subsequent launch is easier than within the
301     // same process.
302     CleanupMigratedHashes(unprotected_pref_names_,
303                           protected_pref_hash_store_.get(),
304                           protected_prefs_.get());
305     CleanupMigratedHashes(protected_pref_names_,
306                           unprotected_pref_hash_store_.get(),
307                           unprotected_prefs_.get());
308     legacy_pref_hash_store_->Reset();
309   }
310 
311   // Hand the processed prefs back to their respective filters.
312   finalize_unprotected_filter_on_load_.Run(unprotected_prefs_.Pass(),
313                                            unprotected_prefs_altered);
314   finalize_protected_filter_on_load_.Run(protected_prefs_.Pass(),
315                                          protected_prefs_altered);
316 
317   if (unprotected_prefs_need_cleanup) {
318     // Schedule a cleanup of the |protected_pref_names_| from the unprotected
319     // prefs once the protected prefs were successfully written to disk (or
320     // do it immediately if |!protected_prefs_altered|).
321     ScheduleSourcePrefStoreCleanup(
322         register_on_successful_protected_store_write_callback_,
323         unprotected_store_cleaner_,
324         protected_pref_names_,
325         protected_prefs_altered);
326   }
327 
328   if (protected_prefs_need_cleanup) {
329     // Schedule a cleanup of the |unprotected_pref_names_| from the protected
330     // prefs once the unprotected prefs were successfully written to disk (or
331     // do it immediately if |!unprotected_prefs_altered|).
332     ScheduleSourcePrefStoreCleanup(
333         register_on_successful_unprotected_store_write_callback_,
334         protected_store_cleaner_,
335         unprotected_pref_names_,
336         unprotected_prefs_altered);
337   }
338 }
339 
340 }  // namespace
341 
SetupTrackedPreferencesMigration(const std::set<std::string> & unprotected_pref_names,const std::set<std::string> & protected_pref_names,const base::Callback<void (const std::string & key)> & unprotected_store_cleaner,const base::Callback<void (const std::string & key)> & protected_store_cleaner,const base::Callback<void (const base::Closure &)> & register_on_successful_unprotected_store_write_callback,const base::Callback<void (const base::Closure &)> & register_on_successful_protected_store_write_callback,scoped_ptr<PrefHashStore> unprotected_pref_hash_store,scoped_ptr<PrefHashStore> protected_pref_hash_store,scoped_ptr<HashStoreContents> legacy_pref_hash_store,InterceptablePrefFilter * unprotected_pref_filter,InterceptablePrefFilter * protected_pref_filter)342 void SetupTrackedPreferencesMigration(
343     const std::set<std::string>& unprotected_pref_names,
344     const std::set<std::string>& protected_pref_names,
345     const base::Callback<void(const std::string& key)>&
346         unprotected_store_cleaner,
347     const base::Callback<void(const std::string& key)>& protected_store_cleaner,
348     const base::Callback<void(const base::Closure&)>&
349         register_on_successful_unprotected_store_write_callback,
350     const base::Callback<void(const base::Closure&)>&
351         register_on_successful_protected_store_write_callback,
352     scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
353     scoped_ptr<PrefHashStore> protected_pref_hash_store,
354     scoped_ptr<HashStoreContents> legacy_pref_hash_store,
355     InterceptablePrefFilter* unprotected_pref_filter,
356     InterceptablePrefFilter* protected_pref_filter) {
357   scoped_refptr<TrackedPreferencesMigrator> prefs_migrator(
358       new TrackedPreferencesMigrator(
359           unprotected_pref_names,
360           protected_pref_names,
361           unprotected_store_cleaner,
362           protected_store_cleaner,
363           register_on_successful_unprotected_store_write_callback,
364           register_on_successful_protected_store_write_callback,
365           unprotected_pref_hash_store.Pass(),
366           protected_pref_hash_store.Pass(),
367           legacy_pref_hash_store.Pass(),
368           unprotected_pref_filter,
369           protected_pref_filter));
370 }
371