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 19namespace { 20 21class 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|. 89void 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. 103void 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|. 120void 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|. 137void 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 207TrackedPreferencesMigrator::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 245TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {} 246 247void 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 266void 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 342void 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