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 3 // found in the LICENSE file. 4 5 #include "sync/engine/conflict_resolver.h" 6 7 #include <list> 8 #include <set> 9 #include <string> 10 11 #include "base/metrics/histogram.h" 12 #include "sync/engine/conflict_util.h" 13 #include "sync/engine/syncer_util.h" 14 #include "sync/internal_api/public/sessions/update_counters.h" 15 #include "sync/sessions/status_controller.h" 16 #include "sync/syncable/directory.h" 17 #include "sync/syncable/mutable_entry.h" 18 #include "sync/syncable/syncable_write_transaction.h" 19 #include "sync/util/cryptographer.h" 20 21 using std::list; 22 using std::set; 23 24 namespace syncer { 25 26 using sessions::StatusController; 27 using syncable::Directory; 28 using syncable::Entry; 29 using syncable::Id; 30 using syncable::MutableEntry; 31 using syncable::WriteTransaction; 32 33 namespace { 34 35 // Returns true iff the set of attachment ids contained in attachment_metadata 36 // matches the set of ids contained in server_attachment_metadata. 37 bool AttachmentMetadataMatches(const MutableEntry& entity) { 38 const sync_pb::AttachmentMetadata& local = entity.GetAttachmentMetadata(); 39 const sync_pb::AttachmentMetadata& server = 40 entity.GetServerAttachmentMetadata(); 41 if (local.record_size() != server.record_size()) { 42 return false; 43 } 44 45 // The order of records in local and server may be different so use a std::set 46 // to determine if they are equivalent. 47 std::set<std::string> local_ids; 48 for (int i = 0; i < local.record_size(); ++i) { 49 const sync_pb::AttachmentMetadataRecord& record = local.record(i); 50 DCHECK(record.is_on_server()); 51 local_ids.insert(record.id().SerializeAsString()); 52 } 53 for (int i = 0; i < server.record_size(); ++i) { 54 const sync_pb::AttachmentMetadataRecord& record = server.record(i); 55 DCHECK(record.is_on_server()); 56 if (local_ids.find(record.id().SerializeAsString()) == local_ids.end()) { 57 return false; 58 } 59 } 60 61 return true; 62 } 63 64 } // namespace 65 66 ConflictResolver::ConflictResolver() { 67 } 68 69 ConflictResolver::~ConflictResolver() { 70 } 71 72 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, 73 const Id& id, 74 const Cryptographer* cryptographer, 75 StatusController* status, 76 UpdateCounters* counters) { 77 MutableEntry entry(trans, syncable::GET_BY_ID, id); 78 // Must be good as the entry won't have been cleaned up. 79 CHECK(entry.good()); 80 81 // This function can only resolve simple conflicts. Simple conflicts have 82 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set. 83 if (!entry.GetIsUnappliedUpdate() || !entry.GetIsUnsynced()) { 84 // This is very unusual, but it can happen in tests. We may be able to 85 // assert NOTREACHED() here when those tests are updated. 86 return; 87 } 88 89 if (entry.GetIsDel() && entry.GetServerIsDel()) { 90 // we've both deleted it, so lets just drop the need to commit/update this 91 // entry. 92 entry.PutIsUnsynced(false); 93 entry.PutIsUnappliedUpdate(false); 94 // we've made changes, but they won't help syncing progress. 95 // METRIC simple conflict resolved by merge. 96 return; 97 } 98 99 // This logic determines "client wins" vs. "server wins" strategy picking. 100 // By the time we get to this point, we rely on the following to be true: 101 // a) We can decrypt both the local and server data (else we'd be in 102 // conflict encryption and not attempting to resolve). 103 // b) All unsynced changes have been re-encrypted with the default key ( 104 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase, 105 // SetDecryptionPassphrase, or RefreshEncryption). 106 // c) Base_server_specifics having a valid datatype means that we received 107 // an undecryptable update that only changed specifics, and since then have 108 // not received any further non-specifics-only or decryptable updates. 109 // d) If the server_specifics match specifics, server_specifics are 110 // encrypted with the default key, and all other visible properties match, 111 // then we can safely ignore the local changes as redundant. 112 // e) Otherwise if the base_server_specifics match the server_specifics, no 113 // functional change must have been made server-side (else 114 // base_server_specifics would have been cleared), and we can therefore 115 // safely ignore the server changes as redundant. 116 // f) Otherwise, it's in general safer to ignore local changes, with the 117 // exception of deletion conflicts (choose to undelete) and conflicts 118 // where the non_unique_name or parent don't match. 119 if (!entry.GetServerIsDel()) { 120 // TODO(nick): The current logic is arbitrary; instead, it ought to be made 121 // consistent with the ModelAssociator behavior for a datatype. It would 122 // be nice if we could route this back to ModelAssociator code to pick one 123 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) 124 // are easily mergeable. 125 // See http://crbug.com/77339. 126 bool name_matches = entry.GetNonUniqueName() == 127 entry.GetServerNonUniqueName(); 128 bool parent_matches = entry.GetParentId() == entry.GetServerParentId(); 129 bool entry_deleted = entry.GetIsDel(); 130 // The position check might fail spuriously if one of the positions was 131 // based on a legacy random suffix, rather than a deterministic one based on 132 // originator_cache_guid and originator_item_id. If an item is being 133 // modified regularly, it shouldn't take long for the suffix and position to 134 // be updated, so such false failures shouldn't be a problem for long. 135 // 136 // Lucky for us, it's OK to be wrong here. The position_matches check is 137 // allowed to return false negatives, as long as it returns no false 138 // positives. 139 bool position_matches = parent_matches && 140 entry.GetServerUniquePosition().Equals(entry.GetUniquePosition()); 141 const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics(); 142 const sync_pb::EntitySpecifics& server_specifics = 143 entry.GetServerSpecifics(); 144 const sync_pb::EntitySpecifics& base_server_specifics = 145 entry.GetBaseServerSpecifics(); 146 std::string decrypted_specifics, decrypted_server_specifics; 147 bool specifics_match = false; 148 bool server_encrypted_with_default_key = false; 149 if (specifics.has_encrypted()) { 150 DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())); 151 decrypted_specifics = cryptographer->DecryptToString( 152 specifics.encrypted()); 153 } else { 154 decrypted_specifics = specifics.SerializeAsString(); 155 } 156 if (server_specifics.has_encrypted()) { 157 server_encrypted_with_default_key = 158 cryptographer->CanDecryptUsingDefaultKey( 159 server_specifics.encrypted()); 160 decrypted_server_specifics = cryptographer->DecryptToString( 161 server_specifics.encrypted()); 162 } else { 163 decrypted_server_specifics = server_specifics.SerializeAsString(); 164 } 165 if (decrypted_server_specifics == decrypted_specifics && 166 server_encrypted_with_default_key == specifics.has_encrypted()) { 167 specifics_match = true; 168 } 169 bool base_server_specifics_match = false; 170 if (server_specifics.has_encrypted() && 171 IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) { 172 std::string decrypted_base_server_specifics; 173 if (!base_server_specifics.has_encrypted()) { 174 decrypted_base_server_specifics = 175 base_server_specifics.SerializeAsString(); 176 } else { 177 decrypted_base_server_specifics = cryptographer->DecryptToString( 178 base_server_specifics.encrypted()); 179 } 180 if (decrypted_server_specifics == decrypted_base_server_specifics) 181 base_server_specifics_match = true; 182 } 183 184 bool attachment_metadata_matches = AttachmentMetadataMatches(entry); 185 if (!entry_deleted && name_matches && parent_matches && specifics_match && 186 position_matches && attachment_metadata_matches) { 187 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " 188 << "changes for: " << entry; 189 conflict_util::IgnoreConflict(&entry); 190 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 191 CHANGES_MATCH, 192 CONFLICT_RESOLUTION_SIZE); 193 } else if (base_server_specifics_match) { 194 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " 195 << " changes for: " << entry; 196 status->increment_num_server_overwrites(); 197 counters->num_server_overwrites++; 198 conflict_util::OverwriteServerChanges(&entry); 199 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 200 IGNORE_ENCRYPTION, 201 CONFLICT_RESOLUTION_SIZE); 202 } else if (entry_deleted || !name_matches || !parent_matches) { 203 // NOTE: The update application logic assumes that conflict resolution 204 // will never result in changes to the local hierarchy. The entry_deleted 205 // and !parent_matches cases here are critical to maintaining that 206 // assumption. 207 conflict_util::OverwriteServerChanges(&entry); 208 status->increment_num_server_overwrites(); 209 counters->num_server_overwrites++; 210 DVLOG(1) << "Resolving simple conflict, overwriting server changes " 211 << "for: " << entry; 212 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 213 OVERWRITE_SERVER, 214 CONFLICT_RESOLUTION_SIZE); 215 } else { 216 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " 217 << entry; 218 conflict_util::IgnoreLocalChanges(&entry); 219 status->increment_num_local_overwrites(); 220 counters->num_local_overwrites++; 221 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 222 OVERWRITE_LOCAL, 223 CONFLICT_RESOLUTION_SIZE); 224 } 225 // Now that we've resolved the conflict, clear the prev server 226 // specifics. 227 entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); 228 } else { // SERVER_IS_DEL is true 229 if (entry.GetIsDir()) { 230 Directory::Metahandles children; 231 trans->directory()->GetChildHandlesById(trans, 232 entry.GetId(), 233 &children); 234 // If a server deleted folder has local contents it should be a hierarchy 235 // conflict. Hierarchy conflicts should not be processed by this 236 // function. 237 DCHECK(children.empty()); 238 } 239 240 // The entry is deleted on the server but still exists locally. 241 // We undelete it by overwriting the server's tombstone with the local 242 // data. 243 conflict_util::OverwriteServerChanges(&entry); 244 status->increment_num_server_overwrites(); 245 counters->num_server_overwrites++; 246 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " 247 << entry; 248 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 249 UNDELETE, 250 CONFLICT_RESOLUTION_SIZE); 251 } 252 } 253 254 void ConflictResolver::ResolveConflicts( 255 syncable::WriteTransaction* trans, 256 const Cryptographer* cryptographer, 257 const std::set<syncable::Id>& simple_conflict_ids, 258 sessions::StatusController* status, 259 UpdateCounters* counters) { 260 // Iterate over simple conflict items. 261 set<Id>::const_iterator it; 262 for (it = simple_conflict_ids.begin(); 263 it != simple_conflict_ids.end(); 264 ++it) { 265 // We don't resolve conflicts for control types here. 266 Entry conflicting_node(trans, syncable::GET_BY_ID, *it); 267 CHECK(conflicting_node.good()); 268 if (IsControlType( 269 GetModelTypeFromSpecifics(conflicting_node.GetSpecifics()))) { 270 continue; 271 } 272 273 ProcessSimpleConflict(trans, *it, cryptographer, status, counters); 274 } 275 return; 276 } 277 278 } // namespace syncer 279