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.
5 #include "components/user_prefs/tracked/tracked_preferences_migration.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 "components/user_prefs/tracked/dictionary_hash_store_contents.h"
14 #include "components/user_prefs/tracked/hash_store_contents.h"
15 #include "components/user_prefs/tracked/interceptable_pref_filter.h"
16 #include "components/user_prefs/tracked/pref_hash_store.h"
17 #include "components/user_prefs/tracked/pref_hash_store_transaction.h"
21 class TrackedPreferencesMigrator
22 : public base::RefCounted
<TrackedPreferencesMigrator
> {
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
);
42 friend class base::RefCounted
<TrackedPreferencesMigrator
>;
45 UNPROTECTED_PREF_FILTER
,
49 ~TrackedPreferencesMigrator();
51 // Stores the data coming in from the filter identified by |id| into this
52 // class and then calls MigrateIfReady();
53 void InterceptFilterOnLoad(
55 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback
&
56 finalize_filter_on_load
,
57 scoped_ptr
<base::DictionaryValue
> prefs
);
59 // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_|
61 void MigrateIfReady();
63 const std::set
<std::string
> unprotected_pref_names_
;
64 const std::set
<std::string
> protected_pref_names_
;
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_
;
73 InterceptablePrefFilter::FinalizeFilterOnLoadCallback
74 finalize_unprotected_filter_on_load_
;
75 InterceptablePrefFilter::FinalizeFilterOnLoadCallback
76 finalize_protected_filter_on_load_
;
78 scoped_ptr
<PrefHashStore
> unprotected_pref_hash_store_
;
79 scoped_ptr
<PrefHashStore
> protected_pref_hash_store_
;
80 scoped_ptr
<HashStoreContents
> legacy_pref_hash_store_
;
82 scoped_ptr
<base::DictionaryValue
> unprotected_prefs_
;
83 scoped_ptr
<base::DictionaryValue
> protected_prefs_
;
85 DISALLOW_COPY_AND_ASSIGN(TrackedPreferencesMigrator
);
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
);
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
));
114 CleanupPrefStore(source_store_cleaner
, keys_to_clean
);
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();
129 transaction
->ClearHash(*it
);
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
))));
153 for (std::set
<std::string
>::const_iterator it
= pref_names
.begin();
154 it
!= pref_names
.end();
156 const std::string
& pref_name
= *it
;
157 const base::Value
* value_in_old_store
= NULL
;
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;
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;
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
);
189 *used_legacy_pref_hashes
= true;
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;
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
235 unprotected_pref_filter
->InterceptNextFilterOnLoad(
236 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad
,
238 UNPROTECTED_PREF_FILTER
));
239 protected_pref_filter
->InterceptNextFilterOnLoad(
240 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad
,
242 PROTECTED_PREF_FILTER
));
245 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
247 void TrackedPreferencesMigrator::InterceptFilterOnLoad(
249 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback
&
250 finalize_filter_on_load
,
251 scoped_ptr
<base::DictionaryValue
> prefs
) {
253 case UNPROTECTED_PREF_FILTER
:
254 finalize_unprotected_filter_on_load_
= finalize_filter_on_load
;
255 unprotected_prefs_
= prefs
.Pass();
257 case PROTECTED_PREF_FILTER
:
258 finalize_protected_filter_on_load_
= finalize_filter_on_load
;
259 protected_prefs_
= prefs
.Pass();
266 void TrackedPreferencesMigrator::MigrateIfReady() {
267 // Wait for both stores to have been read before proceeding.
268 if (!protected_prefs_
|| !unprotected_prefs_
)
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
);
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
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();
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
);
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
);
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
);
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
));