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

Side by Side Diff: chrome/browser/sync/engine/syncer_util.cc

Issue 8770032: [Sync] Implement encryption-aware conflict resolution. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Split out nigori conflict code and rebase Created 9 years 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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/browser/sync/engine/syncer_util.h" 5 #include "chrome/browser/sync/engine/syncer_util.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 #include <string> 9 #include <string>
10 #include <vector> 10 #include <vector>
11 11
12 #include "base/location.h" 12 #include "base/location.h"
13 #include "base/metrics/histogram.h"
13 #include "chrome/browser/sync/engine/conflict_resolver.h" 14 #include "chrome/browser/sync/engine/conflict_resolver.h"
14 #include "chrome/browser/sync/engine/nigori_util.h" 15 #include "chrome/browser/sync/engine/nigori_util.h"
15 #include "chrome/browser/sync/engine/syncer_proto_util.h" 16 #include "chrome/browser/sync/engine/syncer_proto_util.h"
16 #include "chrome/browser/sync/engine/syncer_types.h" 17 #include "chrome/browser/sync/engine/syncer_types.h"
17 #include "chrome/browser/sync/engine/syncproto.h" 18 #include "chrome/browser/sync/engine/syncproto.h"
18 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" 19 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
19 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h" 20 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
20 #include "chrome/browser/sync/protocol/password_specifics.pb.h" 21 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
21 #include "chrome/browser/sync/protocol/sync.pb.h" 22 #include "chrome/browser/sync/protocol/sync.pb.h"
22 #include "chrome/browser/sync/syncable/directory_manager.h" 23 #include "chrome/browser/sync/syncable/directory_manager.h"
23 #include "chrome/browser/sync/syncable/model_type.h" 24 #include "chrome/browser/sync/syncable/model_type.h"
24 #include "chrome/browser/sync/syncable/syncable.h" 25 #include "chrome/browser/sync/syncable/syncable.h"
25 #include "chrome/browser/sync/syncable/syncable_changes_version.h" 26 #include "chrome/browser/sync/syncable/syncable_changes_version.h"
27 #include "chrome/browser/sync/util/cryptographer.h"
26 #include "chrome/browser/sync/util/time.h" 28 #include "chrome/browser/sync/util/time.h"
27 29
28 using syncable::BASE_VERSION; 30 using syncable::BASE_VERSION;
29 using syncable::Blob; 31 using syncable::Blob;
30 using syncable::CHANGES_VERSION; 32 using syncable::CHANGES_VERSION;
31 using syncable::CREATE; 33 using syncable::CREATE;
32 using syncable::CREATE_NEW_UPDATE_ITEM; 34 using syncable::CREATE_NEW_UPDATE_ITEM;
33 using syncable::CTIME; 35 using syncable::CTIME;
34 using syncable::Directory; 36 using syncable::Directory;
35 using syncable::Entry; 37 using syncable::Entry;
38 using syncable::GetModelTypeFromSpecifics;
36 using syncable::GET_BY_HANDLE; 39 using syncable::GET_BY_HANDLE;
37 using syncable::GET_BY_ID; 40 using syncable::GET_BY_ID;
38 using syncable::ID; 41 using syncable::ID;
39 using syncable::IS_DEL; 42 using syncable::IS_DEL;
40 using syncable::IS_DIR; 43 using syncable::IS_DIR;
41 using syncable::IS_UNAPPLIED_UPDATE; 44 using syncable::IS_UNAPPLIED_UPDATE;
42 using syncable::IS_UNSYNCED; 45 using syncable::IS_UNSYNCED;
43 using syncable::Id; 46 using syncable::Id;
47 using syncable::IsRealDataType;
44 using syncable::META_HANDLE; 48 using syncable::META_HANDLE;
45 using syncable::MTIME; 49 using syncable::MTIME;
46 using syncable::MutableEntry; 50 using syncable::MutableEntry;
47 using syncable::NEXT_ID; 51 using syncable::NEXT_ID;
48 using syncable::NON_UNIQUE_NAME; 52 using syncable::NON_UNIQUE_NAME;
53 using syncable::PREV_SERVER_SPECIFICS;
49 using syncable::PARENT_ID; 54 using syncable::PARENT_ID;
50 using syncable::PREV_ID; 55 using syncable::PREV_ID;
51 using syncable::ReadTransaction; 56 using syncable::ReadTransaction;
52 using syncable::SERVER_CTIME; 57 using syncable::SERVER_CTIME;
53 using syncable::SERVER_IS_DEL; 58 using syncable::SERVER_IS_DEL;
54 using syncable::SERVER_IS_DIR; 59 using syncable::SERVER_IS_DIR;
55 using syncable::SERVER_MTIME; 60 using syncable::SERVER_MTIME;
56 using syncable::SERVER_NON_UNIQUE_NAME; 61 using syncable::SERVER_NON_UNIQUE_NAME;
57 using syncable::SERVER_PARENT_ID; 62 using syncable::SERVER_PARENT_ID;
58 using syncable::SERVER_POSITION_IN_PARENT; 63 using syncable::SERVER_POSITION_IN_PARENT;
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 // so as soon as a client is restarted with this datatype encrypted, all 297 // so as soon as a client is restarted with this datatype encrypted, all
293 // the data should be updated as necessary. 298 // the data should be updated as necessary.
294 299
295 // If this fails, something is wrong with the cryptographer, but there's 300 // If this fails, something is wrong with the cryptographer, but there's
296 // nothing we can do about it here. 301 // nothing we can do about it here.
297 syncable::ProcessUnsyncedChangesForEncryption(trans, 302 syncable::ProcessUnsyncedChangesForEncryption(trans,
298 cryptographer); 303 cryptographer);
299 } 304 }
300 } 305 }
301 306
307 // Only apply updates that we can decrypt. If we can't decrypt the update, it
308 // is likely because the passphrase has not arrived yet. Because the
309 // passphrase may not arrive within this GetUpdates, we can't just return
310 // conflict, else we try to perform normal conflict resolution prematurely or
311 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is
312 // treated as a non-blocking conflict. See the description in syncer_types.h.
313 // This prevents any unsynced changes from commiting and postpones conflict
314 // resolution until all data can be decrypted.
315 if (specifics.has_encrypted() &&
316 !cryptographer->CanDecrypt(specifics.encrypted())) {
317 // We can't decrypt this node yet.
318 DVLOG(1) << "Received an undecryptable "
319 << syncable::ModelTypeToString(entry->GetServerModelType())
320 << " update, returning encryption_conflict.";
321 return CONFLICT_ENCRYPTION;
322 } else if (specifics.HasExtension(sync_pb::password) &&
323 entry->Get(UNIQUE_SERVER_TAG).empty()) {
324 // Passwords use their own legacy encryption scheme.
325 const sync_pb::PasswordSpecifics& password =
326 specifics.GetExtension(sync_pb::password);
327 if (!cryptographer->CanDecrypt(password.encrypted())) {
328 DVLOG(1) << "Received an undecryptable password update, returning "
329 << "encryption_conflict.";
330 return CONFLICT_ENCRYPTION;
331 }
332 }
333
302 if (entry->Get(IS_UNSYNCED)) { 334 if (entry->Get(IS_UNSYNCED)) {
303 DVLOG(1) << "Skipping update, returning conflict for: " << id 335 DVLOG(1) << "Skipping update, returning conflict for: " << id
304 << " ; it's unsynced."; 336 << " ; it's unsynced.";
305 return CONFLICT; 337 return CONFLICT;
306 } 338 }
307 if (!entry->Get(SERVER_IS_DEL)) { 339 if (!entry->Get(SERVER_IS_DEL)) {
308 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); 340 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID);
309 Entry parent(trans, GET_BY_ID, new_parent); 341 Entry parent(trans, GET_BY_ID, new_parent);
310 // A note on non-directory parents: 342 // A note on non-directory parents:
311 // We catch most unfixable tree invariant errors at update receipt time, 343 // We catch most unfixable tree invariant errors at update receipt time,
(...skipping 15 matching lines...) Expand all
327 Directory::ChildHandles handles; 359 Directory::ChildHandles handles;
328 trans->directory()->GetChildHandlesById(trans, id, &handles); 360 trans->directory()->GetChildHandlesById(trans, id, &handles);
329 if (!handles.empty()) { 361 if (!handles.empty()) {
330 // If we have still-existing children, then we need to deal with 362 // If we have still-existing children, then we need to deal with
331 // them before we can process this change. 363 // them before we can process this change.
332 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; 364 DVLOG(1) << "Not deleting directory; it's not empty " << *entry;
333 return CONFLICT; 365 return CONFLICT;
334 } 366 }
335 } 367 }
336 368
337 // Only apply updates that we can decrypt. If we can't decrypt the update, it 369 if (specifics.has_encrypted()) {
338 // is likely because the passphrase has not arrived yet. Because the 370 DVLOG(2) << "Received a decryptable "
339 // passphrase may not arrive within this GetUpdates, we can't just return
340 // conflict, else the syncer gets stuck. As such, we return
341 // CONFLICT_ENCRYPTION, which is treated as a non-blocking conflict. See the
342 // description in syncer_types.h.
343 if (specifics.has_encrypted() &&
344 !cryptographer->CanDecrypt(specifics.encrypted())) {
345 // We can't decrypt this node yet.
346 DVLOG(1) << "Received an undecryptable "
347 << syncable::ModelTypeToString(entry->GetServerModelType()) 371 << syncable::ModelTypeToString(entry->GetServerModelType())
348 << " update, returning encryption_conflict."; 372 << " update, applying normally.";
349 return CONFLICT_ENCRYPTION;
350 } else if (specifics.HasExtension(sync_pb::password) &&
351 entry->Get(UNIQUE_SERVER_TAG).empty()) {
352 // Passwords use their own legacy encryption scheme.
353 const sync_pb::PasswordSpecifics& password =
354 specifics.GetExtension(sync_pb::password);
355 if (!cryptographer->CanDecrypt(password.encrypted())) {
356 DVLOG(1) << "Received an undecryptable password update, returning "
357 << "encryption_conflict.";
358 return CONFLICT_ENCRYPTION;
359 }
360 } else { 373 } else {
361 if (specifics.has_encrypted()) { 374 DVLOG(2) << "Received an unencrypted "
362 DVLOG(2) << "Received a decryptable " 375 << syncable::ModelTypeToString(entry->GetServerModelType())
363 << syncable::ModelTypeToString(entry->GetServerModelType()) 376 << " update, applying normally.";
364 << " update, applying normally.";
365 } else {
366 DVLOG(2) << "Received an unencrypted "
367 << syncable::ModelTypeToString(entry->GetServerModelType())
368 << " update, applying normally.";
369 }
370 } 377 }
371 378
372 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); 379 SyncerUtil::UpdateLocalDataFromServerData(trans, entry);
373 380
374 return SUCCESS; 381 return SUCCESS;
375 } 382 }
376 383
377 namespace { 384 namespace {
378 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, 385 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally,
379 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based 386 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 // commit we don't apply it as time differences may occur. 473 // commit we don't apply it as time differences may occur.
467 if (update.version() > target->Get(BASE_VERSION)) { 474 if (update.version() > target->Get(BASE_VERSION)) {
468 target->Put(IS_UNAPPLIED_UPDATE, true); 475 target->Put(IS_UNAPPLIED_UPDATE, true);
469 } 476 }
470 } 477 }
471 478
472 // Creates a new Entry iff no Entry exists with the given id. 479 // Creates a new Entry iff no Entry exists with the given id.
473 // static 480 // static
474 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, 481 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans,
475 const syncable::Id& id) { 482 const syncable::Id& id) {
476 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, id); 483 syncable::MutableEntry entry(trans, GET_BY_ID, id);
477 if (!entry.good()) { 484 if (!entry.good()) {
478 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, 485 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM,
479 id); 486 id);
480 } 487 }
481 } 488 }
482 489
483 // static 490 // static
484 void SyncerUtil::SplitServerInformationIntoNewEntry( 491 void SyncerUtil::SplitServerInformationIntoNewEntry(
485 syncable::WriteTransaction* trans, 492 syncable::WriteTransaction* trans,
486 syncable::MutableEntry* entry) { 493 syncable::MutableEntry* entry) {
(...skipping 14 matching lines...) Expand all
501 // static 508 // static
502 void SyncerUtil::UpdateLocalDataFromServerData( 509 void SyncerUtil::UpdateLocalDataFromServerData(
503 syncable::WriteTransaction* trans, 510 syncable::WriteTransaction* trans,
504 syncable::MutableEntry* entry) { 511 syncable::MutableEntry* entry) {
505 DCHECK(!entry->Get(IS_UNSYNCED)); 512 DCHECK(!entry->Get(IS_UNSYNCED));
506 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); 513 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE));
507 514
508 DVLOG(2) << "Updating entry : " << *entry; 515 DVLOG(2) << "Updating entry : " << *entry;
509 // Start by setting the properties that determine the model_type. 516 // Start by setting the properties that determine the model_type.
510 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); 517 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS));
518 // Clear the previous server specifics now that we're applying successfully.
519 entry->Put(PREV_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
511 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); 520 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR));
512 // This strange dance around the IS_DEL flag avoids problems when setting 521 // This strange dance around the IS_DEL flag avoids problems when setting
513 // the name. 522 // the name.
514 // TODO(chron): Is this still an issue? Unit test this codepath. 523 // TODO(chron): Is this still an issue? Unit test this codepath.
515 if (entry->Get(SERVER_IS_DEL)) { 524 if (entry->Get(SERVER_IS_DEL)) {
516 entry->Put(IS_DEL, true); 525 entry->Put(IS_DEL, true);
517 } else { 526 } else {
518 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); 527 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME));
519 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); 528 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID));
520 CHECK(entry->Put(IS_DEL, false)); 529 CHECK(entry->Put(IS_DEL, false));
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
760 if (update.version() < target->Get(SERVER_VERSION)) { 769 if (update.version() < target->Get(SERVER_VERSION)) {
761 LOG(WARNING) << "Update older than current server version for " 770 LOG(WARNING) << "Update older than current server version for "
762 << *target << " Update:" 771 << *target << " Update:"
763 << SyncerProtoUtil::SyncEntityDebugString(update); 772 << SyncerProtoUtil::SyncEntityDebugString(update);
764 return VERIFY_SUCCESS; // Expected in new sync protocol. 773 return VERIFY_SUCCESS; // Expected in new sync protocol.
765 } 774 }
766 return VERIFY_UNDECIDED; 775 return VERIFY_UNDECIDED;
767 } 776 }
768 777
769 } // namespace browser_sync 778 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698