Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(508)

Side by Side Diff: sync/engine/conflict_resolver.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/engine/conflict_resolver.h ('k') | sync/engine/conflict_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 // e) Except for the case of extensions and apps, where we want uninstalls to
120 // win over local modifications to avoid "back from the dead" reinstalls.
121 if (!entry.GetServerIsDel()) {
122 // TODO(nick): The current logic is arbitrary; instead, it ought to be made
123 // consistent with the ModelAssociator behavior for a datatype. It would
124 // be nice if we could route this back to ModelAssociator code to pick one
125 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill)
126 // are easily mergeable.
127 // See http://crbug.com/77339.
128 bool name_matches = entry.GetNonUniqueName() ==
129 entry.GetServerNonUniqueName();
130 // The parent is implicit type root folder or the parent ID matches.
131 bool parent_matches = entry.GetServerParentId().IsNull() ||
132 entry.GetParentId() == entry.GetServerParentId();
133 bool entry_deleted = entry.GetIsDel();
134 // The position check might fail spuriously if one of the positions was
135 // based on a legacy random suffix, rather than a deterministic one based on
136 // originator_cache_guid and originator_item_id. If an item is being
137 // modified regularly, it shouldn't take long for the suffix and position to
138 // be updated, so such false failures shouldn't be a problem for long.
139 //
140 // Lucky for us, it's OK to be wrong here. The position_matches check is
141 // allowed to return false negatives, as long as it returns no false
142 // positives.
143 bool position_matches = parent_matches &&
144 entry.GetServerUniquePosition().Equals(entry.GetUniquePosition());
145 const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics();
146 const sync_pb::EntitySpecifics& server_specifics =
147 entry.GetServerSpecifics();
148 const sync_pb::EntitySpecifics& base_server_specifics =
149 entry.GetBaseServerSpecifics();
150 std::string decrypted_specifics, decrypted_server_specifics;
151 bool specifics_match = false;
152 bool server_encrypted_with_default_key = false;
153 if (specifics.has_encrypted()) {
154 DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted()));
155 decrypted_specifics = cryptographer->DecryptToString(
156 specifics.encrypted());
157 } else {
158 decrypted_specifics = specifics.SerializeAsString();
159 }
160 if (server_specifics.has_encrypted()) {
161 server_encrypted_with_default_key =
162 cryptographer->CanDecryptUsingDefaultKey(
163 server_specifics.encrypted());
164 decrypted_server_specifics = cryptographer->DecryptToString(
165 server_specifics.encrypted());
166 } else {
167 decrypted_server_specifics = server_specifics.SerializeAsString();
168 }
169 if (decrypted_server_specifics == decrypted_specifics &&
170 server_encrypted_with_default_key == specifics.has_encrypted()) {
171 specifics_match = true;
172 }
173 bool base_server_specifics_match = false;
174 if (server_specifics.has_encrypted() &&
175 IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) {
176 std::string decrypted_base_server_specifics;
177 if (!base_server_specifics.has_encrypted()) {
178 decrypted_base_server_specifics =
179 base_server_specifics.SerializeAsString();
180 } else {
181 decrypted_base_server_specifics = cryptographer->DecryptToString(
182 base_server_specifics.encrypted());
183 }
184 if (decrypted_server_specifics == decrypted_base_server_specifics)
185 base_server_specifics_match = true;
186 }
187
188 bool attachment_metadata_matches = AttachmentMetadataMatches(entry);
189 if (!entry_deleted && name_matches && parent_matches && specifics_match &&
190 position_matches && attachment_metadata_matches) {
191 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring "
192 << "changes for: " << entry;
193 conflict_util::IgnoreConflict(&entry);
194 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
195 CHANGES_MATCH,
196 CONFLICT_RESOLUTION_SIZE);
197 } else if (base_server_specifics_match) {
198 DVLOG(1) << "Resolving simple conflict, ignoring server encryption "
199 << " changes for: " << entry;
200 status->increment_num_server_overwrites();
201 counters->num_server_overwrites++;
202 conflict_util::OverwriteServerChanges(&entry);
203 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
204 IGNORE_ENCRYPTION,
205 CONFLICT_RESOLUTION_SIZE);
206 } else if (entry_deleted || !name_matches || !parent_matches) {
207 // NOTE: The update application logic assumes that conflict resolution
208 // will never result in changes to the local hierarchy. The entry_deleted
209 // and !parent_matches cases here are critical to maintaining that
210 // assumption.
211 conflict_util::OverwriteServerChanges(&entry);
212 status->increment_num_server_overwrites();
213 counters->num_server_overwrites++;
214 DVLOG(1) << "Resolving simple conflict, overwriting server changes "
215 << "for: " << entry;
216 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
217 OVERWRITE_SERVER,
218 CONFLICT_RESOLUTION_SIZE);
219 } else {
220 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
221 << entry;
222 conflict_util::IgnoreLocalChanges(&entry);
223 status->increment_num_local_overwrites();
224 counters->num_local_overwrites++;
225 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
226 OVERWRITE_LOCAL,
227 CONFLICT_RESOLUTION_SIZE);
228 }
229 // Now that we've resolved the conflict, clear the prev server
230 // specifics.
231 entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics());
232 } else { // SERVER_IS_DEL is true
233 ModelType type = entry.GetModelType();
234 if (type == EXTENSIONS || type == APPS) {
235 // Ignore local changes for extensions/apps when server had a delete, to
236 // avoid unwanted reinstall of an uninstalled extension.
237 DVLOG(1) << "Resolving simple conflict, ignoring local changes for "
238 << "extension/app: " << entry;
239 conflict_util::IgnoreLocalChanges(&entry);
240 status->increment_num_local_overwrites();
241 counters->num_local_overwrites++;
242 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
243 OVERWRITE_LOCAL,
244 CONFLICT_RESOLUTION_SIZE);
245 } else {
246 if (entry.GetIsDir()) {
247 Directory::Metahandles children;
248 trans->directory()->GetChildHandlesById(trans,
249 entry.GetId(),
250 &children);
251 // If a server deleted folder has local contents it should be a
252 // hierarchy conflict. Hierarchy conflicts should not be processed by
253 // this function.
254 DCHECK(children.empty());
255 }
256
257 // The entry is deleted on the server but still exists locally.
258 // We undelete it by overwriting the server's tombstone with the local
259 // data.
260 conflict_util::OverwriteServerChanges(&entry);
261 status->increment_num_server_overwrites();
262 counters->num_server_overwrites++;
263 DVLOG(1) << "Resolving simple conflict, undeleting server entry: "
264 << entry;
265 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
266 UNDELETE,
267 CONFLICT_RESOLUTION_SIZE);
268 }
269 }
270 }
271
272 void ConflictResolver::ResolveConflicts(
273 syncable::WriteTransaction* trans,
274 const Cryptographer* cryptographer,
275 const std::set<syncable::Id>& simple_conflict_ids,
276 sessions::StatusController* status,
277 UpdateCounters* counters) {
278 // Iterate over simple conflict items.
279 set<Id>::const_iterator it;
280 for (it = simple_conflict_ids.begin();
281 it != simple_conflict_ids.end();
282 ++it) {
283 // We don't resolve conflicts for control types here.
284 Entry conflicting_node(trans, syncable::GET_BY_ID, *it);
285 CHECK(conflicting_node.good());
286 if (IsControlType(
287 GetModelTypeFromSpecifics(conflicting_node.GetSpecifics()))) {
288 continue;
289 }
290
291 ProcessSimpleConflict(trans, *it, cryptographer, status, counters);
292 }
293 return;
294 }
295
296 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/engine/conflict_resolver.h ('k') | sync/engine/conflict_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698