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.
5 #include "sync/engine/conflict_resolver.h"
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"
26 using sessions::StatusController
;
27 using syncable::Directory
;
28 using syncable::Entry
;
30 using syncable::MutableEntry
;
31 using syncable::WriteTransaction
;
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()) {
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());
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()) {
66 ConflictResolver::ConflictResolver() {
69 ConflictResolver::~ConflictResolver() {
72 void ConflictResolver::ProcessSimpleConflict(WriteTransaction
* trans
,
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.
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.
89 if (entry
.GetIsDel() && entry
.GetServerIsDel()) {
90 // we've both deleted it, so lets just drop the need to commit/update this
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.
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.
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
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());
154 decrypted_specifics
= specifics
.SerializeAsString();
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());
163 decrypted_server_specifics
= server_specifics
.SerializeAsString();
165 if (decrypted_server_specifics
== decrypted_specifics
&&
166 server_encrypted_with_default_key
== specifics
.has_encrypted()) {
167 specifics_match
= true;
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();
177 decrypted_base_server_specifics
= cryptographer
->DecryptToString(
178 base_server_specifics
.encrypted());
180 if (decrypted_server_specifics
== decrypted_base_server_specifics
)
181 base_server_specifics_match
= true;
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",
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",
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
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 "
212 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
214 CONFLICT_RESOLUTION_SIZE
);
216 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
218 conflict_util::IgnoreLocalChanges(&entry
);
219 status
->increment_num_local_overwrites();
220 counters
->num_local_overwrites
++;
221 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
223 CONFLICT_RESOLUTION_SIZE
);
225 // Now that we've resolved the conflict, clear the prev server
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
,
234 // If a server deleted folder has local contents it should be a hierarchy
235 // conflict. Hierarchy conflicts should not be processed by this
237 DCHECK(children
.empty());
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
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: "
248 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
250 CONFLICT_RESOLUTION_SIZE
);
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();
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());
269 GetModelTypeFromSpecifics(conflicting_node
.GetSpecifics()))) {
273 ProcessSimpleConflict(trans
, *it
, cryptographer
, status
, counters
);
278 } // namespace syncer