| 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/write_transaction.h" | 20 #include "sync/syncable/write_transaction.h" |
| 21 #include "sync/util/cryptographer.h" | 21 #include "sync/util/cryptographer.h" |
| 22 | 22 |
| 23 using std::list; | 23 using std::list; |
| 24 using std::map; | 24 using std::map; |
| 25 using std::set; | 25 using std::set; |
| 26 | 26 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 40 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; | 40 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; |
| 41 | 41 |
| 42 } // namespace | 42 } // namespace |
| 43 | 43 |
| 44 ConflictResolver::ConflictResolver() { | 44 ConflictResolver::ConflictResolver() { |
| 45 } | 45 } |
| 46 | 46 |
| 47 ConflictResolver::~ConflictResolver() { | 47 ConflictResolver::~ConflictResolver() { |
| 48 } | 48 } |
| 49 | 49 |
| 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 | 50 ConflictResolver::ProcessSimpleConflictResult |
| 70 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 51 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
| 71 const Id& id, | 52 const Id& id, |
| 72 const Cryptographer* cryptographer, | 53 const Cryptographer* cryptographer, |
| 73 StatusController* status) { | 54 StatusController* status) { |
| 74 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 55 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
| 75 // Must be good as the entry won't have been cleaned up. | 56 // Must be good as the entry won't have been cleaned up. |
| 76 CHECK(entry.good()); | 57 CHECK(entry.good()); |
| 77 | 58 |
| 78 // This function can only resolve simple conflicts. Simple conflicts have | 59 // 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 = | 194 decrypted_base_server_specifics = |
| 214 base_server_specifics.SerializeAsString(); | 195 base_server_specifics.SerializeAsString(); |
| 215 } else { | 196 } else { |
| 216 decrypted_base_server_specifics = cryptographer->DecryptToString( | 197 decrypted_base_server_specifics = cryptographer->DecryptToString( |
| 217 base_server_specifics.encrypted()); | 198 base_server_specifics.encrypted()); |
| 218 } | 199 } |
| 219 if (decrypted_server_specifics == decrypted_base_server_specifics) | 200 if (decrypted_server_specifics == decrypted_base_server_specifics) |
| 220 base_server_specifics_match = true; | 201 base_server_specifics_match = true; |
| 221 } | 202 } |
| 222 | 203 |
| 223 // We manually merge nigori data. | 204 if (!entry_deleted && name_matches && parent_matches && specifics_match && |
| 224 if (entry.GetModelType() == NIGORI) { | 205 !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, trans); | |
| 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 " | 206 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
| 274 << "changes for: " << entry; | 207 << "changes for: " << entry; |
| 275 // This unsets both IS_UNSYNCED and IS_UNAPPLIED_UPDATE, and sets the | 208 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", | 209 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 286 CHANGES_MATCH, | 210 CHANGES_MATCH, |
| 287 CONFLICT_RESOLUTION_SIZE); | 211 CONFLICT_RESOLUTION_SIZE); |
| 288 } else if (base_server_specifics_match) { | 212 } else if (base_server_specifics_match) { |
| 289 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 213 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
| 290 << " changes for: " << entry; | 214 << " changes for: " << entry; |
| 291 status->increment_num_server_overwrites(); | 215 status->increment_num_server_overwrites(); |
| 292 OverwriteServerChanges(trans, &entry); | 216 OverwriteServerChanges(&entry); |
| 293 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 217 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 294 IGNORE_ENCRYPTION, | 218 IGNORE_ENCRYPTION, |
| 295 CONFLICT_RESOLUTION_SIZE); | 219 CONFLICT_RESOLUTION_SIZE); |
| 296 } else if (entry_deleted || !name_matches || !parent_matches) { | 220 } else if (entry_deleted || !name_matches || !parent_matches) { |
| 297 OverwriteServerChanges(trans, &entry); | 221 OverwriteServerChanges(&entry); |
| 298 status->increment_num_server_overwrites(); | 222 status->increment_num_server_overwrites(); |
| 299 DVLOG(1) << "Resolving simple conflict, overwriting server changes " | 223 DVLOG(1) << "Resolving simple conflict, overwriting server changes " |
| 300 << "for: " << entry; | 224 << "for: " << entry; |
| 301 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 225 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 302 OVERWRITE_SERVER, | 226 OVERWRITE_SERVER, |
| 303 CONFLICT_RESOLUTION_SIZE); | 227 CONFLICT_RESOLUTION_SIZE); |
| 304 } else { | 228 } else { |
| 305 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " | 229 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " |
| 306 << entry; | 230 << entry; |
| 307 IgnoreLocalChanges(&entry); | 231 IgnoreLocalChanges(&entry); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 331 } | 255 } |
| 332 } | 256 } |
| 333 | 257 |
| 334 // The entry is deleted on the server but still exists locally. | 258 // The entry is deleted on the server but still exists locally. |
| 335 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { | 259 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { |
| 336 // If we've got a client-unique tag, we can undelete while retaining | 260 // If we've got a client-unique tag, we can undelete while retaining |
| 337 // our present ID. | 261 // our present ID. |
| 338 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " | 262 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 " | 263 "know to re-create, client-tagged items should revert to version 0 " |
| 340 "when server-deleted."; | 264 "when server-deleted."; |
| 341 OverwriteServerChanges(trans, &entry); | 265 OverwriteServerChanges(&entry); |
| 342 status->increment_num_server_overwrites(); | 266 status->increment_num_server_overwrites(); |
| 343 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " | 267 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " |
| 344 << entry; | 268 << entry; |
| 345 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 269 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 346 OVERWRITE_SERVER, | 270 OVERWRITE_SERVER, |
| 347 CONFLICT_RESOLUTION_SIZE); | 271 CONFLICT_RESOLUTION_SIZE); |
| 348 // Clobber the versions, just in case the above DCHECK is violated. | 272 // Clobber the versions, just in case the above DCHECK is violated. |
| 349 entry.Put(syncable::SERVER_VERSION, 0); | 273 entry.Put(syncable::SERVER_VERSION, 0); |
| 350 entry.Put(syncable::BASE_VERSION, 0); | 274 entry.Put(syncable::BASE_VERSION, 0); |
| 351 } else { | 275 } else { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 forward_progress = true; | 333 forward_progress = true; |
| 410 break; | 334 break; |
| 411 } | 335 } |
| 412 processed_items.insert(id); | 336 processed_items.insert(id); |
| 413 } | 337 } |
| 414 } | 338 } |
| 415 return forward_progress; | 339 return forward_progress; |
| 416 } | 340 } |
| 417 | 341 |
| 418 } // namespace syncer | 342 } // namespace syncer |
| OLD | NEW |