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

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

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

Powered by Google App Engine
This is Rietveld 408576698