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

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

Issue 10832286: sync: Introduce control data types (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Respond to Tim's comments Created 8 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 | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698