OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sync/engine/conflict_resolver.h" | 5 #include "components/sync/engine_impl/conflict_resolver.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
8 #include <set> | 8 #include <set> |
9 #include <string> | 9 #include <string> |
10 | 10 |
11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
12 #include "sync/engine/conflict_util.h" | 12 #include "components/sync/base/cryptographer.h" |
13 #include "sync/engine/syncer_util.h" | 13 #include "components/sync/engine_impl/conflict_util.h" |
14 #include "sync/internal_api/public/sessions/update_counters.h" | 14 #include "components/sync/engine_impl/syncer_util.h" |
15 #include "sync/sessions/status_controller.h" | 15 #include "components/sync/sessions/update_counters.h" |
16 #include "sync/syncable/directory.h" | 16 #include "components/sync/sessions_impl/status_controller.h" |
17 #include "sync/syncable/mutable_entry.h" | 17 #include "components/sync/syncable/directory.h" |
18 #include "sync/syncable/syncable_write_transaction.h" | 18 #include "components/sync/syncable/mutable_entry.h" |
19 #include "sync/util/cryptographer.h" | 19 #include "components/sync/syncable/syncable_write_transaction.h" |
20 | 20 |
21 using std::list; | 21 using std::list; |
22 using std::set; | 22 using std::set; |
23 | 23 |
24 namespace syncer { | 24 namespace syncer { |
25 | 25 |
26 using sessions::StatusController; | 26 using sessions::StatusController; |
27 using syncable::Directory; | 27 using syncable::Directory; |
28 using syncable::Entry; | 28 using syncable::Entry; |
29 using syncable::Id; | 29 using syncable::Id; |
(...skipping 26 matching lines...) Expand all Loading... |
56 if (local_ids.find(record.id().SerializeAsString()) == local_ids.end()) { | 56 if (local_ids.find(record.id().SerializeAsString()) == local_ids.end()) { |
57 return false; | 57 return false; |
58 } | 58 } |
59 } | 59 } |
60 | 60 |
61 return true; | 61 return true; |
62 } | 62 } |
63 | 63 |
64 } // namespace | 64 } // namespace |
65 | 65 |
66 ConflictResolver::ConflictResolver() { | 66 ConflictResolver::ConflictResolver() {} |
67 } | |
68 | 67 |
69 ConflictResolver::~ConflictResolver() { | 68 ConflictResolver::~ConflictResolver() {} |
70 } | |
71 | 69 |
72 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 70 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
73 const Id& id, | 71 const Id& id, |
74 const Cryptographer* cryptographer, | 72 const Cryptographer* cryptographer, |
75 StatusController* status, | 73 StatusController* status, |
76 UpdateCounters* counters) { | 74 UpdateCounters* counters) { |
77 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 75 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
78 // Must be good as the entry won't have been cleaned up. | 76 // Must be good as the entry won't have been cleaned up. |
79 CHECK(entry.good()); | 77 CHECK(entry.good()); |
80 | 78 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 // where the non_unique_name or parent don't match. | 116 // where the non_unique_name or parent don't match. |
119 // e) Except for the case of extensions and apps, where we want uninstalls to | 117 // e) Except for the case of extensions and apps, where we want uninstalls to |
120 // win over local modifications to avoid "back from the dead" reinstalls. | 118 // win over local modifications to avoid "back from the dead" reinstalls. |
121 if (!entry.GetServerIsDel()) { | 119 if (!entry.GetServerIsDel()) { |
122 // TODO(nick): The current logic is arbitrary; instead, it ought to be made | 120 // TODO(nick): The current logic is arbitrary; instead, it ought to be made |
123 // consistent with the ModelAssociator behavior for a datatype. It would | 121 // consistent with the ModelAssociator behavior for a datatype. It would |
124 // be nice if we could route this back to ModelAssociator code to pick one | 122 // be nice if we could route this back to ModelAssociator code to pick one |
125 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) | 123 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) |
126 // are easily mergeable. | 124 // are easily mergeable. |
127 // See http://crbug.com/77339. | 125 // See http://crbug.com/77339. |
128 bool name_matches = entry.GetNonUniqueName() == | 126 bool name_matches = |
129 entry.GetServerNonUniqueName(); | 127 entry.GetNonUniqueName() == entry.GetServerNonUniqueName(); |
130 // The parent is implicit type root folder or the parent ID matches. | 128 // The parent is implicit type root folder or the parent ID matches. |
131 bool parent_matches = entry.GetServerParentId().IsNull() || | 129 bool parent_matches = entry.GetServerParentId().IsNull() || |
132 entry.GetParentId() == entry.GetServerParentId(); | 130 entry.GetParentId() == entry.GetServerParentId(); |
133 bool entry_deleted = entry.GetIsDel(); | 131 bool entry_deleted = entry.GetIsDel(); |
134 // The position check might fail spuriously if one of the positions was | 132 // The position check might fail spuriously if one of the positions was |
135 // based on a legacy random suffix, rather than a deterministic one based on | 133 // based on a legacy random suffix, rather than a deterministic one based on |
136 // originator_cache_guid and originator_item_id. If an item is being | 134 // originator_cache_guid and originator_item_id. If an item is being |
137 // modified regularly, it shouldn't take long for the suffix and position to | 135 // modified regularly, it shouldn't take long for the suffix and position to |
138 // be updated, so such false failures shouldn't be a problem for long. | 136 // be updated, so such false failures shouldn't be a problem for long. |
139 // | 137 // |
140 // Lucky for us, it's OK to be wrong here. The position_matches check is | 138 // Lucky for us, it's OK to be wrong here. The position_matches check is |
141 // allowed to return false negatives, as long as it returns no false | 139 // allowed to return false negatives, as long as it returns no false |
142 // positives. | 140 // positives. |
143 bool position_matches = parent_matches && | 141 bool position_matches = |
144 entry.GetServerUniquePosition().Equals(entry.GetUniquePosition()); | 142 parent_matches && |
| 143 entry.GetServerUniquePosition().Equals(entry.GetUniquePosition()); |
145 const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics(); | 144 const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics(); |
146 const sync_pb::EntitySpecifics& server_specifics = | 145 const sync_pb::EntitySpecifics& server_specifics = |
147 entry.GetServerSpecifics(); | 146 entry.GetServerSpecifics(); |
148 const sync_pb::EntitySpecifics& base_server_specifics = | 147 const sync_pb::EntitySpecifics& base_server_specifics = |
149 entry.GetBaseServerSpecifics(); | 148 entry.GetBaseServerSpecifics(); |
150 std::string decrypted_specifics, decrypted_server_specifics; | 149 std::string decrypted_specifics, decrypted_server_specifics; |
151 bool specifics_match = false; | 150 bool specifics_match = false; |
152 bool server_encrypted_with_default_key = false; | 151 bool server_encrypted_with_default_key = false; |
153 if (specifics.has_encrypted()) { | 152 if (specifics.has_encrypted()) { |
154 DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())); | 153 DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())); |
155 decrypted_specifics = cryptographer->DecryptToString( | 154 decrypted_specifics = |
156 specifics.encrypted()); | 155 cryptographer->DecryptToString(specifics.encrypted()); |
157 } else { | 156 } else { |
158 decrypted_specifics = specifics.SerializeAsString(); | 157 decrypted_specifics = specifics.SerializeAsString(); |
159 } | 158 } |
160 if (server_specifics.has_encrypted()) { | 159 if (server_specifics.has_encrypted()) { |
161 server_encrypted_with_default_key = | 160 server_encrypted_with_default_key = |
162 cryptographer->CanDecryptUsingDefaultKey( | 161 cryptographer->CanDecryptUsingDefaultKey( |
163 server_specifics.encrypted()); | 162 server_specifics.encrypted()); |
164 decrypted_server_specifics = cryptographer->DecryptToString( | 163 decrypted_server_specifics = |
165 server_specifics.encrypted()); | 164 cryptographer->DecryptToString(server_specifics.encrypted()); |
166 } else { | 165 } else { |
167 decrypted_server_specifics = server_specifics.SerializeAsString(); | 166 decrypted_server_specifics = server_specifics.SerializeAsString(); |
168 } | 167 } |
169 if (decrypted_server_specifics == decrypted_specifics && | 168 if (decrypted_server_specifics == decrypted_specifics && |
170 server_encrypted_with_default_key == specifics.has_encrypted()) { | 169 server_encrypted_with_default_key == specifics.has_encrypted()) { |
171 specifics_match = true; | 170 specifics_match = true; |
172 } | 171 } |
173 bool base_server_specifics_match = false; | 172 bool base_server_specifics_match = false; |
174 if (server_specifics.has_encrypted() && | 173 if (server_specifics.has_encrypted() && |
175 IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) { | 174 IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) { |
176 std::string decrypted_base_server_specifics; | 175 std::string decrypted_base_server_specifics; |
177 if (!base_server_specifics.has_encrypted()) { | 176 if (!base_server_specifics.has_encrypted()) { |
178 decrypted_base_server_specifics = | 177 decrypted_base_server_specifics = |
179 base_server_specifics.SerializeAsString(); | 178 base_server_specifics.SerializeAsString(); |
180 } else { | 179 } else { |
181 decrypted_base_server_specifics = cryptographer->DecryptToString( | 180 decrypted_base_server_specifics = |
182 base_server_specifics.encrypted()); | 181 cryptographer->DecryptToString(base_server_specifics.encrypted()); |
183 } | 182 } |
184 if (decrypted_server_specifics == decrypted_base_server_specifics) | 183 if (decrypted_server_specifics == decrypted_base_server_specifics) |
185 base_server_specifics_match = true; | 184 base_server_specifics_match = true; |
186 } | 185 } |
187 | 186 |
188 bool attachment_metadata_matches = AttachmentMetadataMatches(entry); | 187 bool attachment_metadata_matches = AttachmentMetadataMatches(entry); |
189 if (!entry_deleted && name_matches && parent_matches && specifics_match && | 188 if (!entry_deleted && name_matches && parent_matches && specifics_match && |
190 position_matches && attachment_metadata_matches) { | 189 position_matches && attachment_metadata_matches) { |
191 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " | 190 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
192 << "changes for: " << entry; | 191 << "changes for: " << entry; |
193 conflict_util::IgnoreConflict(&entry); | 192 conflict_util::IgnoreConflict(&entry); |
194 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 193 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", CHANGES_MATCH, |
195 CHANGES_MATCH, | |
196 CONFLICT_RESOLUTION_SIZE); | 194 CONFLICT_RESOLUTION_SIZE); |
197 } else if (base_server_specifics_match) { | 195 } else if (base_server_specifics_match) { |
198 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 196 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
199 << " changes for: " << entry; | 197 << " changes for: " << entry; |
200 status->increment_num_server_overwrites(); | 198 status->increment_num_server_overwrites(); |
201 counters->num_server_overwrites++; | 199 counters->num_server_overwrites++; |
202 conflict_util::OverwriteServerChanges(&entry); | 200 conflict_util::OverwriteServerChanges(&entry); |
203 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 201 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", IGNORE_ENCRYPTION, |
204 IGNORE_ENCRYPTION, | |
205 CONFLICT_RESOLUTION_SIZE); | 202 CONFLICT_RESOLUTION_SIZE); |
206 } else if (entry_deleted || !name_matches || !parent_matches) { | 203 } else if (entry_deleted || !name_matches || !parent_matches) { |
207 // NOTE: The update application logic assumes that conflict resolution | 204 // NOTE: The update application logic assumes that conflict resolution |
208 // will never result in changes to the local hierarchy. The entry_deleted | 205 // will never result in changes to the local hierarchy. The entry_deleted |
209 // and !parent_matches cases here are critical to maintaining that | 206 // and !parent_matches cases here are critical to maintaining that |
210 // assumption. | 207 // assumption. |
211 conflict_util::OverwriteServerChanges(&entry); | 208 conflict_util::OverwriteServerChanges(&entry); |
212 status->increment_num_server_overwrites(); | 209 status->increment_num_server_overwrites(); |
213 counters->num_server_overwrites++; | 210 counters->num_server_overwrites++; |
214 DVLOG(1) << "Resolving simple conflict, overwriting server changes " | 211 DVLOG(1) << "Resolving simple conflict, overwriting server changes " |
215 << "for: " << entry; | 212 << "for: " << entry; |
216 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 213 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", OVERWRITE_SERVER, |
217 OVERWRITE_SERVER, | |
218 CONFLICT_RESOLUTION_SIZE); | 214 CONFLICT_RESOLUTION_SIZE); |
219 } else { | 215 } else { |
220 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " | 216 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " |
221 << entry; | 217 << entry; |
222 conflict_util::IgnoreLocalChanges(&entry); | 218 conflict_util::IgnoreLocalChanges(&entry); |
223 status->increment_num_local_overwrites(); | 219 status->increment_num_local_overwrites(); |
224 counters->num_local_overwrites++; | 220 counters->num_local_overwrites++; |
225 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 221 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", OVERWRITE_LOCAL, |
226 OVERWRITE_LOCAL, | |
227 CONFLICT_RESOLUTION_SIZE); | 222 CONFLICT_RESOLUTION_SIZE); |
228 } | 223 } |
229 // Now that we've resolved the conflict, clear the prev server | 224 // Now that we've resolved the conflict, clear the prev server |
230 // specifics. | 225 // specifics. |
231 entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); | 226 entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); |
232 } else { // SERVER_IS_DEL is true | 227 } else { // SERVER_IS_DEL is true |
233 ModelType type = entry.GetModelType(); | 228 ModelType type = entry.GetModelType(); |
234 if (type == EXTENSIONS || type == APPS) { | 229 if (type == EXTENSIONS || type == APPS) { |
235 // Ignore local changes for extensions/apps when server had a delete, to | 230 // Ignore local changes for extensions/apps when server had a delete, to |
236 // avoid unwanted reinstall of an uninstalled extension. | 231 // avoid unwanted reinstall of an uninstalled extension. |
237 DVLOG(1) << "Resolving simple conflict, ignoring local changes for " | 232 DVLOG(1) << "Resolving simple conflict, ignoring local changes for " |
238 << "extension/app: " << entry; | 233 << "extension/app: " << entry; |
239 conflict_util::IgnoreLocalChanges(&entry); | 234 conflict_util::IgnoreLocalChanges(&entry); |
240 status->increment_num_local_overwrites(); | 235 status->increment_num_local_overwrites(); |
241 counters->num_local_overwrites++; | 236 counters->num_local_overwrites++; |
242 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 237 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", OVERWRITE_LOCAL, |
243 OVERWRITE_LOCAL, | |
244 CONFLICT_RESOLUTION_SIZE); | 238 CONFLICT_RESOLUTION_SIZE); |
245 } else { | 239 } else { |
246 if (entry.GetIsDir()) { | 240 if (entry.GetIsDir()) { |
247 Directory::Metahandles children; | 241 Directory::Metahandles children; |
248 trans->directory()->GetChildHandlesById(trans, | 242 trans->directory()->GetChildHandlesById(trans, entry.GetId(), |
249 entry.GetId(), | |
250 &children); | 243 &children); |
251 // If a server deleted folder has local contents it should be a | 244 // If a server deleted folder has local contents it should be a |
252 // hierarchy conflict. Hierarchy conflicts should not be processed by | 245 // hierarchy conflict. Hierarchy conflicts should not be processed by |
253 // this function. | 246 // this function. |
254 DCHECK(children.empty()); | 247 DCHECK(children.empty()); |
255 } | 248 } |
256 | 249 |
257 // The entry is deleted on the server but still exists locally. | 250 // The entry is deleted on the server but still exists locally. |
258 // We undelete it by overwriting the server's tombstone with the local | 251 // We undelete it by overwriting the server's tombstone with the local |
259 // data. | 252 // data. |
260 conflict_util::OverwriteServerChanges(&entry); | 253 conflict_util::OverwriteServerChanges(&entry); |
261 status->increment_num_server_overwrites(); | 254 status->increment_num_server_overwrites(); |
262 counters->num_server_overwrites++; | 255 counters->num_server_overwrites++; |
263 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " | 256 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " |
264 << entry; | 257 << entry; |
265 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 258 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", UNDELETE, |
266 UNDELETE, | |
267 CONFLICT_RESOLUTION_SIZE); | 259 CONFLICT_RESOLUTION_SIZE); |
268 } | 260 } |
269 } | 261 } |
270 } | 262 } |
271 | 263 |
272 void ConflictResolver::ResolveConflicts( | 264 void ConflictResolver::ResolveConflicts( |
273 syncable::WriteTransaction* trans, | 265 syncable::WriteTransaction* trans, |
274 const Cryptographer* cryptographer, | 266 const Cryptographer* cryptographer, |
275 const std::set<syncable::Id>& simple_conflict_ids, | 267 const std::set<syncable::Id>& simple_conflict_ids, |
276 sessions::StatusController* status, | 268 sessions::StatusController* status, |
277 UpdateCounters* counters) { | 269 UpdateCounters* counters) { |
278 // Iterate over simple conflict items. | 270 // Iterate over simple conflict items. |
279 set<Id>::const_iterator it; | 271 set<Id>::const_iterator it; |
280 for (it = simple_conflict_ids.begin(); | 272 for (it = simple_conflict_ids.begin(); it != simple_conflict_ids.end(); |
281 it != simple_conflict_ids.end(); | |
282 ++it) { | 273 ++it) { |
283 // We don't resolve conflicts for control types here. | 274 // We don't resolve conflicts for control types here. |
284 Entry conflicting_node(trans, syncable::GET_BY_ID, *it); | 275 Entry conflicting_node(trans, syncable::GET_BY_ID, *it); |
285 CHECK(conflicting_node.good()); | 276 CHECK(conflicting_node.good()); |
286 if (IsControlType( | 277 if (IsControlType( |
287 GetModelTypeFromSpecifics(conflicting_node.GetSpecifics()))) { | 278 GetModelTypeFromSpecifics(conflicting_node.GetSpecifics()))) { |
288 continue; | 279 continue; |
289 } | 280 } |
290 | 281 |
291 ProcessSimpleConflict(trans, *it, cryptographer, status, counters); | 282 ProcessSimpleConflict(trans, *it, cryptographer, status, counters); |
292 } | 283 } |
293 return; | 284 return; |
294 } | 285 } |
295 | 286 |
296 } // namespace syncer | 287 } // namespace syncer |
OLD | NEW |