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