OLD | NEW |
| (Empty) |
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 "components/user_prefs/tracked/tracked_preferences_migration.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/callback.h" | |
11 #include "base/macros.h" | |
12 #include "base/memory/ref_counted.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/values.h" | |
15 #include "components/user_prefs/tracked/dictionary_hash_store_contents.h" | |
16 #include "components/user_prefs/tracked/hash_store_contents.h" | |
17 #include "components/user_prefs/tracked/interceptable_pref_filter.h" | |
18 #include "components/user_prefs/tracked/pref_hash_store.h" | |
19 #include "components/user_prefs/tracked/pref_hash_store_transaction.h" | |
20 | |
21 namespace { | |
22 | |
23 class TrackedPreferencesMigrator | |
24 : public base::RefCounted<TrackedPreferencesMigrator> { | |
25 public: | |
26 TrackedPreferencesMigrator( | |
27 const std::set<std::string>& unprotected_pref_names, | |
28 const std::set<std::string>& protected_pref_names, | |
29 const base::Callback<void(const std::string& key)>& | |
30 unprotected_store_cleaner, | |
31 const base::Callback<void(const std::string& key)>& | |
32 protected_store_cleaner, | |
33 const base::Callback<void(const base::Closure&)>& | |
34 register_on_successful_unprotected_store_write_callback, | |
35 const base::Callback<void(const base::Closure&)>& | |
36 register_on_successful_protected_store_write_callback, | |
37 std::unique_ptr<PrefHashStore> unprotected_pref_hash_store, | |
38 std::unique_ptr<PrefHashStore> protected_pref_hash_store, | |
39 InterceptablePrefFilter* unprotected_pref_filter, | |
40 InterceptablePrefFilter* protected_pref_filter); | |
41 | |
42 private: | |
43 friend class base::RefCounted<TrackedPreferencesMigrator>; | |
44 | |
45 enum PrefFilterID { | |
46 UNPROTECTED_PREF_FILTER, | |
47 PROTECTED_PREF_FILTER | |
48 }; | |
49 | |
50 ~TrackedPreferencesMigrator(); | |
51 | |
52 // Stores the data coming in from the filter identified by |id| into this | |
53 // class and then calls MigrateIfReady(); | |
54 void InterceptFilterOnLoad( | |
55 PrefFilterID id, | |
56 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback& | |
57 finalize_filter_on_load, | |
58 std::unique_ptr<base::DictionaryValue> prefs); | |
59 | |
60 // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_| | |
61 // have been set. | |
62 void MigrateIfReady(); | |
63 | |
64 const std::set<std::string> unprotected_pref_names_; | |
65 const std::set<std::string> protected_pref_names_; | |
66 | |
67 const base::Callback<void(const std::string& key)> unprotected_store_cleaner_; | |
68 const base::Callback<void(const std::string& key)> protected_store_cleaner_; | |
69 const base::Callback<void(const base::Closure&)> | |
70 register_on_successful_unprotected_store_write_callback_; | |
71 const base::Callback<void(const base::Closure&)> | |
72 register_on_successful_protected_store_write_callback_; | |
73 | |
74 InterceptablePrefFilter::FinalizeFilterOnLoadCallback | |
75 finalize_unprotected_filter_on_load_; | |
76 InterceptablePrefFilter::FinalizeFilterOnLoadCallback | |
77 finalize_protected_filter_on_load_; | |
78 | |
79 std::unique_ptr<PrefHashStore> unprotected_pref_hash_store_; | |
80 std::unique_ptr<PrefHashStore> protected_pref_hash_store_; | |
81 | |
82 std::unique_ptr<base::DictionaryValue> unprotected_prefs_; | |
83 std::unique_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 DictionaryHashStoreContents dictionary_contents(origin_pref_store); | |
124 std::unique_ptr<PrefHashStoreTransaction> transaction( | |
125 origin_pref_hash_store->BeginTransaction(&dictionary_contents)); | |
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 bool* old_store_needs_cleanup, | |
142 bool* new_store_altered) { | |
143 const base::DictionaryValue* old_hash_store_contents = | |
144 DictionaryHashStoreContents(old_store).GetContents(); | |
145 DictionaryHashStoreContents dictionary_contents(new_store); | |
146 std::unique_ptr<PrefHashStoreTransaction> new_hash_store_transaction( | |
147 new_hash_store->BeginTransaction(&dictionary_contents)); | |
148 | |
149 for (std::set<std::string>::const_iterator it = pref_names.begin(); | |
150 it != pref_names.end(); | |
151 ++it) { | |
152 const std::string& pref_name = *it; | |
153 const base::Value* value_in_old_store = NULL; | |
154 | |
155 // If the destination does not have a hash for this pref we will | |
156 // unconditionally attempt to move it. | |
157 bool destination_hash_missing = | |
158 !new_hash_store_transaction->HasHash(pref_name); | |
159 // If we migrate the value we will also attempt to migrate the hash. | |
160 bool migrated_value = false; | |
161 if (old_store->Get(pref_name, &value_in_old_store)) { | |
162 // Whether this value ends up being copied below or was left behind by a | |
163 // previous incomplete migration, it should be cleaned up. | |
164 *old_store_needs_cleanup = true; | |
165 | |
166 if (!new_store->Get(pref_name, NULL)) { | |
167 // Copy the value from |old_store| to |new_store| rather than moving it | |
168 // to avoid data loss should |old_store| be flushed to disk without | |
169 // |new_store| having equivalently been successfully flushed to disk | |
170 // (e.g., on crash or in cases where |new_store| is read-only following | |
171 // a read error on startup). | |
172 new_store->Set(pref_name, value_in_old_store->DeepCopy()); | |
173 migrated_value = true; | |
174 *new_store_altered = true; | |
175 } | |
176 } | |
177 | |
178 if (destination_hash_missing || migrated_value) { | |
179 const base::Value* old_hash = NULL; | |
180 if (old_hash_store_contents) | |
181 old_hash_store_contents->Get(pref_name, &old_hash); | |
182 if (old_hash) { | |
183 new_hash_store_transaction->ImportHash(pref_name, old_hash); | |
184 *new_store_altered = true; | |
185 } else if (!destination_hash_missing) { | |
186 // Do not allow values to be migrated without MACs if the destination | |
187 // already has a MAC (http://crbug.com/414554). Remove the migrated | |
188 // value in order to provide the same no-op behaviour as if the pref was | |
189 // added to the wrong file when there was already a value for | |
190 // |pref_name| in |new_store|. | |
191 new_store->Remove(pref_name, NULL); | |
192 *new_store_altered = true; | |
193 } | |
194 } | |
195 } | |
196 } | |
197 | |
198 TrackedPreferencesMigrator::TrackedPreferencesMigrator( | |
199 const std::set<std::string>& unprotected_pref_names, | |
200 const std::set<std::string>& protected_pref_names, | |
201 const base::Callback<void(const std::string& key)>& | |
202 unprotected_store_cleaner, | |
203 const base::Callback<void(const std::string& key)>& protected_store_cleaner, | |
204 const base::Callback<void(const base::Closure&)>& | |
205 register_on_successful_unprotected_store_write_callback, | |
206 const base::Callback<void(const base::Closure&)>& | |
207 register_on_successful_protected_store_write_callback, | |
208 std::unique_ptr<PrefHashStore> unprotected_pref_hash_store, | |
209 std::unique_ptr<PrefHashStore> protected_pref_hash_store, | |
210 InterceptablePrefFilter* unprotected_pref_filter, | |
211 InterceptablePrefFilter* protected_pref_filter) | |
212 : unprotected_pref_names_(unprotected_pref_names), | |
213 protected_pref_names_(protected_pref_names), | |
214 unprotected_store_cleaner_(unprotected_store_cleaner), | |
215 protected_store_cleaner_(protected_store_cleaner), | |
216 register_on_successful_unprotected_store_write_callback_( | |
217 register_on_successful_unprotected_store_write_callback), | |
218 register_on_successful_protected_store_write_callback_( | |
219 register_on_successful_protected_store_write_callback), | |
220 unprotected_pref_hash_store_(std::move(unprotected_pref_hash_store)), | |
221 protected_pref_hash_store_(std::move(protected_pref_hash_store)) { | |
222 // The callbacks bound below will own this TrackedPreferencesMigrator by | |
223 // reference. | |
224 unprotected_pref_filter->InterceptNextFilterOnLoad( | |
225 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad, | |
226 this, | |
227 UNPROTECTED_PREF_FILTER)); | |
228 protected_pref_filter->InterceptNextFilterOnLoad( | |
229 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad, | |
230 this, | |
231 PROTECTED_PREF_FILTER)); | |
232 } | |
233 | |
234 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {} | |
235 | |
236 void TrackedPreferencesMigrator::InterceptFilterOnLoad( | |
237 PrefFilterID id, | |
238 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback& | |
239 finalize_filter_on_load, | |
240 std::unique_ptr<base::DictionaryValue> prefs) { | |
241 switch (id) { | |
242 case UNPROTECTED_PREF_FILTER: | |
243 finalize_unprotected_filter_on_load_ = finalize_filter_on_load; | |
244 unprotected_prefs_ = std::move(prefs); | |
245 break; | |
246 case PROTECTED_PREF_FILTER: | |
247 finalize_protected_filter_on_load_ = finalize_filter_on_load; | |
248 protected_prefs_ = std::move(prefs); | |
249 break; | |
250 } | |
251 | |
252 MigrateIfReady(); | |
253 } | |
254 | |
255 void TrackedPreferencesMigrator::MigrateIfReady() { | |
256 // Wait for both stores to have been read before proceeding. | |
257 if (!protected_prefs_ || !unprotected_prefs_) | |
258 return; | |
259 | |
260 bool protected_prefs_need_cleanup = false; | |
261 bool unprotected_prefs_altered = false; | |
262 MigratePrefsFromOldToNewStore( | |
263 unprotected_pref_names_, protected_prefs_.get(), unprotected_prefs_.get(), | |
264 unprotected_pref_hash_store_.get(), &protected_prefs_need_cleanup, | |
265 &unprotected_prefs_altered); | |
266 bool unprotected_prefs_need_cleanup = false; | |
267 bool protected_prefs_altered = false; | |
268 MigratePrefsFromOldToNewStore( | |
269 protected_pref_names_, unprotected_prefs_.get(), protected_prefs_.get(), | |
270 protected_pref_hash_store_.get(), &unprotected_prefs_need_cleanup, | |
271 &protected_prefs_altered); | |
272 | |
273 if (!unprotected_prefs_altered && !protected_prefs_altered) { | |
274 // Clean up any MACs that might have been previously migrated from the | |
275 // various stores. It's safe to leave them behind for a little while as they | |
276 // will be ignored unless the corresponding value is _also_ present. The | |
277 // cleanup must be deferred until the MACs have been written to their target | |
278 // stores, and doing so in a subsequent launch is easier than within the | |
279 // same process. | |
280 CleanupMigratedHashes(unprotected_pref_names_, | |
281 protected_pref_hash_store_.get(), | |
282 protected_prefs_.get()); | |
283 CleanupMigratedHashes(protected_pref_names_, | |
284 unprotected_pref_hash_store_.get(), | |
285 unprotected_prefs_.get()); | |
286 } | |
287 | |
288 // Hand the processed prefs back to their respective filters. | |
289 finalize_unprotected_filter_on_load_.Run(std::move(unprotected_prefs_), | |
290 unprotected_prefs_altered); | |
291 finalize_protected_filter_on_load_.Run(std::move(protected_prefs_), | |
292 protected_prefs_altered); | |
293 | |
294 if (unprotected_prefs_need_cleanup) { | |
295 // Schedule a cleanup of the |protected_pref_names_| from the unprotected | |
296 // prefs once the protected prefs were successfully written to disk (or | |
297 // do it immediately if |!protected_prefs_altered|). | |
298 ScheduleSourcePrefStoreCleanup( | |
299 register_on_successful_protected_store_write_callback_, | |
300 unprotected_store_cleaner_, | |
301 protected_pref_names_, | |
302 protected_prefs_altered); | |
303 } | |
304 | |
305 if (protected_prefs_need_cleanup) { | |
306 // Schedule a cleanup of the |unprotected_pref_names_| from the protected | |
307 // prefs once the unprotected prefs were successfully written to disk (or | |
308 // do it immediately if |!unprotected_prefs_altered|). | |
309 ScheduleSourcePrefStoreCleanup( | |
310 register_on_successful_unprotected_store_write_callback_, | |
311 protected_store_cleaner_, | |
312 unprotected_pref_names_, | |
313 unprotected_prefs_altered); | |
314 } | |
315 } | |
316 | |
317 } // namespace | |
318 | |
319 void SetupTrackedPreferencesMigration( | |
320 const std::set<std::string>& unprotected_pref_names, | |
321 const std::set<std::string>& protected_pref_names, | |
322 const base::Callback<void(const std::string& key)>& | |
323 unprotected_store_cleaner, | |
324 const base::Callback<void(const std::string& key)>& protected_store_cleaner, | |
325 const base::Callback<void(const base::Closure&)>& | |
326 register_on_successful_unprotected_store_write_callback, | |
327 const base::Callback<void(const base::Closure&)>& | |
328 register_on_successful_protected_store_write_callback, | |
329 std::unique_ptr<PrefHashStore> unprotected_pref_hash_store, | |
330 std::unique_ptr<PrefHashStore> protected_pref_hash_store, | |
331 InterceptablePrefFilter* unprotected_pref_filter, | |
332 InterceptablePrefFilter* protected_pref_filter) { | |
333 scoped_refptr<TrackedPreferencesMigrator> prefs_migrator( | |
334 new TrackedPreferencesMigrator( | |
335 unprotected_pref_names, protected_pref_names, | |
336 unprotected_store_cleaner, protected_store_cleaner, | |
337 register_on_successful_unprotected_store_write_callback, | |
338 register_on_successful_protected_store_write_callback, | |
339 std::move(unprotected_pref_hash_store), | |
340 std::move(protected_pref_hash_store), unprotected_pref_filter, | |
341 protected_pref_filter)); | |
342 } | |
OLD | NEW |