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