Home | History | Annotate | Download | only in tracked
      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|.
     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.
    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|.
    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|.
    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 
    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 
    245 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
    246 
    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 
    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 
    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