| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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 "sync/engine/conflict_resolver.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <list> | 8 #include <list> |
| 9 #include <map> | 9 #include <map> |
| 10 #include <set> | 10 #include <set> |
| 11 | 11 |
| 12 #include "base/location.h" | 12 #include "base/location.h" |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "sync/engine/conflict_util.h" |
| 14 #include "sync/engine/syncer.h" | 15 #include "sync/engine/syncer.h" |
| 15 #include "sync/engine/syncer_util.h" | 16 #include "sync/engine/syncer_util.h" |
| 16 #include "sync/protocol/nigori_specifics.pb.h" | |
| 17 #include "sync/sessions/status_controller.h" | 17 #include "sync/sessions/status_controller.h" |
| 18 #include "sync/syncable/directory.h" | 18 #include "sync/syncable/directory.h" |
| 19 #include "sync/syncable/mutable_entry.h" | 19 #include "sync/syncable/mutable_entry.h" |
| 20 #include "sync/syncable/nigori_handler.h" | 20 #include "sync/syncable/nigori_handler.h" |
| 21 #include "sync/syncable/write_transaction.h" | 21 #include "sync/syncable/write_transaction.h" |
| 22 #include "sync/util/cryptographer.h" | 22 #include "sync/util/cryptographer.h" |
| 23 | 23 |
| 24 using std::list; | 24 using std::list; |
| 25 using std::map; | 25 using std::map; |
| 26 using std::set; | 26 using std::set; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 41 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; | 41 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; |
| 42 | 42 |
| 43 } // namespace | 43 } // namespace |
| 44 | 44 |
| 45 ConflictResolver::ConflictResolver() { | 45 ConflictResolver::ConflictResolver() { |
| 46 } | 46 } |
| 47 | 47 |
| 48 ConflictResolver::~ConflictResolver() { | 48 ConflictResolver::~ConflictResolver() { |
| 49 } | 49 } |
| 50 | 50 |
| 51 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) { | |
| 52 // An update matches local actions, merge the changes. | |
| 53 // This is a little fishy because we don't actually merge them. | |
| 54 // In the future we should do a 3-way merge. | |
| 55 // With IS_UNSYNCED false, changes should be merged. | |
| 56 entry->Put(syncable::IS_UNSYNCED, false); | |
| 57 } | |
| 58 | |
| 59 void ConflictResolver::OverwriteServerChanges(WriteTransaction* trans, | |
| 60 MutableEntry * entry) { | |
| 61 // This is similar to an overwrite from the old client. | |
| 62 // This is equivalent to a scenario where we got the update before we'd | |
| 63 // made our local client changes. | |
| 64 // TODO(chron): This is really a general property clobber. We clobber | |
| 65 // the server side property. Perhaps we should actually do property merging. | |
| 66 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION)); | |
| 67 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false); | |
| 68 } | |
| 69 | |
| 70 ConflictResolver::ProcessSimpleConflictResult | 51 ConflictResolver::ProcessSimpleConflictResult |
| 71 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 52 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
| 72 const Id& id, | 53 const Id& id, |
| 73 const Cryptographer* cryptographer, | 54 const Cryptographer* cryptographer, |
| 74 StatusController* status) { | 55 StatusController* status) { |
| 75 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 56 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
| 76 // Must be good as the entry won't have been cleaned up. | 57 // Must be good as the entry won't have been cleaned up. |
| 77 CHECK(entry.good()); | 58 CHECK(entry.good()); |
| 78 | 59 |
| 79 // This function can only resolve simple conflicts. Simple conflicts have | 60 // This function can only resolve simple conflicts. Simple conflicts have |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 decrypted_base_server_specifics = | 195 decrypted_base_server_specifics = |
| 215 base_server_specifics.SerializeAsString(); | 196 base_server_specifics.SerializeAsString(); |
| 216 } else { | 197 } else { |
| 217 decrypted_base_server_specifics = cryptographer->DecryptToString( | 198 decrypted_base_server_specifics = cryptographer->DecryptToString( |
| 218 base_server_specifics.encrypted()); | 199 base_server_specifics.encrypted()); |
| 219 } | 200 } |
| 220 if (decrypted_server_specifics == decrypted_base_server_specifics) | 201 if (decrypted_server_specifics == decrypted_base_server_specifics) |
| 221 base_server_specifics_match = true; | 202 base_server_specifics_match = true; |
| 222 } | 203 } |
| 223 | 204 |
| 224 // We manually merge nigori data. | 205 if (!entry_deleted && name_matches && parent_matches && specifics_match && |
| 225 if (entry.GetModelType() == NIGORI) { | 206 !needs_reinsertion) { |
| 226 // Create a new set of specifics based on the server specifics (which | |
| 227 // preserves their encryption keys). | |
| 228 sync_pb::EntitySpecifics specifics = | |
| 229 entry.Get(syncable::SERVER_SPECIFICS); | |
| 230 sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori(); | |
| 231 // Store the merged set of encrypted types (cryptographer->Update(..) will | |
| 232 // have merged the local types already). | |
| 233 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( | |
| 234 server_nigori, | |
| 235 trans); | |
| 236 // The cryptographer has the both the local and remote encryption keys | |
| 237 // (added at cryptographer->Update(..) time). | |
| 238 // If the cryptographer is ready, then it already merged both sets of keys | |
| 239 // and we can store them back in. In that case, the remote key was already | |
| 240 // part of the local keybag, so we preserve the local key as the default | |
| 241 // (including whether it's an explicit key). | |
| 242 // If the cryptographer is not ready, then the user will have to provide | |
| 243 // the passphrase to decrypt the pending keys. When they do so, the | |
| 244 // SetDecryptionPassphrase code will act based on whether the server | |
| 245 // update has an explicit passphrase or not. | |
| 246 // - If the server had an explicit passphrase, that explicit passphrase | |
| 247 // will be preserved as the default encryption key. | |
| 248 // - If the server did not have an explicit passphrase, we assume the | |
| 249 // local passphrase is the most up to date and preserve the local | |
| 250 // default encryption key marked as an implicit passphrase. | |
| 251 // This works fine except for the case where we had locally set an | |
| 252 // explicit passphrase. In that case the nigori node will have the default | |
| 253 // key based on the local explicit passphassphrase, but will not have it | |
| 254 // marked as explicit. To fix this we'd have to track whether we have a | |
| 255 // explicit passphrase or not separate from the nigori, which would | |
| 256 // introduce even more complexity, so we leave it up to the user to | |
| 257 // reset that passphrase as an explicit one via settings. The goal here | |
| 258 // is to ensure both sets of encryption keys are preserved. | |
| 259 if (cryptographer->is_ready()) { | |
| 260 cryptographer->GetKeys(server_nigori->mutable_encrypted()); | |
| 261 server_nigori->set_using_explicit_passphrase( | |
| 262 entry.Get(syncable::SPECIFICS).nigori(). | |
| 263 using_explicit_passphrase()); | |
| 264 } | |
| 265 // We deliberately leave the server's device information. This client will | |
| 266 // add its own device information on restart. | |
| 267 entry.Put(syncable::SPECIFICS, specifics); | |
| 268 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " << entry; | |
| 269 status->increment_num_server_overwrites(); | |
| 270 OverwriteServerChanges(trans, &entry); | |
| 271 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | |
| 272 NIGORI_MERGE, | |
| 273 CONFLICT_RESOLUTION_SIZE); | |
| 274 } else if (!entry_deleted && name_matches && parent_matches && | |
| 275 specifics_match && !needs_reinsertion) { | |
| 276 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " | 207 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
| 277 << "changes for: " << entry; | 208 << "changes for: " << entry; |
| 278 // This unsets both IS_UNSYNCED and IS_UNAPPLIED_UPDATE, and sets the | 209 IgnoreConflict(&entry); |
| 279 // BASE_VERSION to match the SERVER_VERSION. If we didn't also unset | |
| 280 // IS_UNAPPLIED_UPDATE, then we would lose unsynced positional data from | |
| 281 // adjacent entries when the server update gets applied and the item is | |
| 282 // re-inserted into the PREV_ID/NEXT_ID linked list. This is primarily | |
| 283 // an issue because we commit after applying updates, and is most | |
| 284 // commonly seen when positional changes are made while a passphrase | |
| 285 // is required (and hence there will be many encryption conflicts). | |
| 286 OverwriteServerChanges(trans, &entry); | |
| 287 IgnoreLocalChanges(&entry); | |
| 288 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 210 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 289 CHANGES_MATCH, | 211 CHANGES_MATCH, |
| 290 CONFLICT_RESOLUTION_SIZE); | 212 CONFLICT_RESOLUTION_SIZE); |
| 291 } else if (base_server_specifics_match) { | 213 } else if (base_server_specifics_match) { |
| 292 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 214 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
| 293 << " changes for: " << entry; | 215 << " changes for: " << entry; |
| 294 status->increment_num_server_overwrites(); | 216 status->increment_num_server_overwrites(); |
| 295 OverwriteServerChanges(trans, &entry); | 217 OverwriteServerChanges(&entry); |
| 296 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 218 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 297 IGNORE_ENCRYPTION, | 219 IGNORE_ENCRYPTION, |
| 298 CONFLICT_RESOLUTION_SIZE); | 220 CONFLICT_RESOLUTION_SIZE); |
| 299 } else if (entry_deleted || !name_matches || !parent_matches) { | 221 } else if (entry_deleted || !name_matches || !parent_matches) { |
| 300 OverwriteServerChanges(trans, &entry); | 222 OverwriteServerChanges(&entry); |
| 301 status->increment_num_server_overwrites(); | 223 status->increment_num_server_overwrites(); |
| 302 DVLOG(1) << "Resolving simple conflict, overwriting server changes " | 224 DVLOG(1) << "Resolving simple conflict, overwriting server changes " |
| 303 << "for: " << entry; | 225 << "for: " << entry; |
| 304 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 226 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 305 OVERWRITE_SERVER, | 227 OVERWRITE_SERVER, |
| 306 CONFLICT_RESOLUTION_SIZE); | 228 CONFLICT_RESOLUTION_SIZE); |
| 307 } else { | 229 } else { |
| 308 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " | 230 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " |
| 309 << entry; | 231 << entry; |
| 310 IgnoreLocalChanges(&entry); | 232 IgnoreLocalChanges(&entry); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 334 } | 256 } |
| 335 } | 257 } |
| 336 | 258 |
| 337 // The entry is deleted on the server but still exists locally. | 259 // The entry is deleted on the server but still exists locally. |
| 338 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { | 260 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { |
| 339 // If we've got a client-unique tag, we can undelete while retaining | 261 // If we've got a client-unique tag, we can undelete while retaining |
| 340 // our present ID. | 262 // our present ID. |
| 341 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " | 263 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " |
| 342 "know to re-create, client-tagged items should revert to version 0 " | 264 "know to re-create, client-tagged items should revert to version 0 " |
| 343 "when server-deleted."; | 265 "when server-deleted."; |
| 344 OverwriteServerChanges(trans, &entry); | 266 OverwriteServerChanges(&entry); |
| 345 status->increment_num_server_overwrites(); | 267 status->increment_num_server_overwrites(); |
| 346 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " | 268 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " |
| 347 << entry; | 269 << entry; |
| 348 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 270 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 349 OVERWRITE_SERVER, | 271 OVERWRITE_SERVER, |
| 350 CONFLICT_RESOLUTION_SIZE); | 272 CONFLICT_RESOLUTION_SIZE); |
| 351 // Clobber the versions, just in case the above DCHECK is violated. | 273 // Clobber the versions, just in case the above DCHECK is violated. |
| 352 entry.Put(syncable::SERVER_VERSION, 0); | 274 entry.Put(syncable::SERVER_VERSION, 0); |
| 353 entry.Put(syncable::BASE_VERSION, 0); | 275 entry.Put(syncable::BASE_VERSION, 0); |
| 354 } else { | 276 } else { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 377 // Iterate over simple conflict items. | 299 // Iterate over simple conflict items. |
| 378 set<Id>::const_iterator conflicting_item_it; | 300 set<Id>::const_iterator conflicting_item_it; |
| 379 set<Id> processed_items; | 301 set<Id> processed_items; |
| 380 for (conflicting_item_it = progress.SimpleConflictingItemsBegin(); | 302 for (conflicting_item_it = progress.SimpleConflictingItemsBegin(); |
| 381 conflicting_item_it != progress.SimpleConflictingItemsEnd(); | 303 conflicting_item_it != progress.SimpleConflictingItemsEnd(); |
| 382 ++conflicting_item_it) { | 304 ++conflicting_item_it) { |
| 383 Id id = *conflicting_item_it; | 305 Id id = *conflicting_item_it; |
| 384 if (processed_items.count(id) > 0) | 306 if (processed_items.count(id) > 0) |
| 385 continue; | 307 continue; |
| 386 | 308 |
| 309 // We don't resolve conflicts for control types here. |
| 310 Entry conflicting_node(trans, syncable::GET_BY_ID, id); |
| 311 CHECK(conflicting_node.good()); |
| 312 if (IsControlType( |
| 313 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { |
| 314 continue; |
| 315 } |
| 316 |
| 387 // We have a simple conflict. In order check if positions have changed, | 317 // We have a simple conflict. In order check if positions have changed, |
| 388 // we need to process conflicting predecessors before successors. Traverse | 318 // we need to process conflicting predecessors before successors. Traverse |
| 389 // backwards through all continuous conflicting predecessors, building a | 319 // backwards through all continuous conflicting predecessors, building a |
| 390 // stack of items to resolve in predecessor->successor order, then process | 320 // stack of items to resolve in predecessor->successor order, then process |
| 391 // each item individually. | 321 // each item individually. |
| 392 list<Id> predecessors; | 322 list<Id> predecessors; |
| 393 Id prev_id = id; | 323 Id prev_id = id; |
| 394 do { | 324 do { |
| 395 predecessors.push_back(prev_id); | 325 predecessors.push_back(prev_id); |
| 396 Entry entry(trans, syncable::GET_BY_ID, prev_id); | 326 Entry entry(trans, syncable::GET_BY_ID, prev_id); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 412 forward_progress = true; | 342 forward_progress = true; |
| 413 break; | 343 break; |
| 414 } | 344 } |
| 415 processed_items.insert(id); | 345 processed_items.insert(id); |
| 416 } | 346 } |
| 417 } | 347 } |
| 418 return forward_progress; | 348 return forward_progress; |
| 419 } | 349 } |
| 420 | 350 |
| 421 } // namespace syncer | 351 } // namespace syncer |
| OLD | NEW |