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

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: Fix test Created 8 years, 12 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) 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::BASE_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 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 // as well, so as soon as a client is restarted with this datatype marked 299 // as well, so as soon as a client is restarted with this datatype marked
295 // for encryption, all the data should be updated as necessary. 300 // for encryption, all the data should be updated as necessary.
296 301
297 // If this fails, something is wrong with the cryptographer, but there's 302 // If this fails, something is wrong with the cryptographer, but there's
298 // nothing we can do about it here. 303 // nothing we can do about it here.
299 syncable::ProcessUnsyncedChangesForEncryption(trans, 304 syncable::ProcessUnsyncedChangesForEncryption(trans,
300 cryptographer); 305 cryptographer);
301 } 306 }
302 } 307 }
303 308
309 // Only apply updates that we can decrypt. If we can't decrypt the update, it
310 // is likely because the passphrase has not arrived yet. Because the
311 // passphrase may not arrive within this GetUpdates, we can't just return
312 // conflict, else we try to perform normal conflict resolution prematurely or
313 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is
314 // treated as a non-blocking conflict. See the description in syncer_types.h.
315 // This prevents any unsynced changes from commiting and postpones conflict
316 // resolution until all data can be decrypted.
317 if (specifics.has_encrypted() &&
318 !cryptographer->CanDecrypt(specifics.encrypted())) {
319 // We can't decrypt this node yet.
320 DVLOG(1) << "Received an undecryptable "
321 << syncable::ModelTypeToString(entry->GetServerModelType())
322 << " update, returning encryption_conflict.";
323 return CONFLICT_ENCRYPTION;
324 } else if (specifics.HasExtension(sync_pb::password) &&
325 entry->Get(UNIQUE_SERVER_TAG).empty()) {
326 // Passwords use their own legacy encryption scheme.
327 const sync_pb::PasswordSpecifics& password =
328 specifics.GetExtension(sync_pb::password);
329 if (!cryptographer->CanDecrypt(password.encrypted())) {
330 DVLOG(1) << "Received an undecryptable password update, returning "
331 << "encryption_conflict.";
332 return CONFLICT_ENCRYPTION;
333 }
334 }
335
304 if (entry->Get(IS_UNSYNCED)) { 336 if (entry->Get(IS_UNSYNCED)) {
305 DVLOG(1) << "Skipping update, returning conflict for: " << id 337 DVLOG(1) << "Skipping update, returning conflict for: " << id
306 << " ; it's unsynced."; 338 << " ; it's unsynced.";
307 return CONFLICT; 339 return CONFLICT;
308 } 340 }
309 if (!entry->Get(SERVER_IS_DEL)) { 341 if (!entry->Get(SERVER_IS_DEL)) {
310 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID); 342 syncable::Id new_parent = entry->Get(SERVER_PARENT_ID);
311 Entry parent(trans, GET_BY_ID, new_parent); 343 Entry parent(trans, GET_BY_ID, new_parent);
312 // A note on non-directory parents: 344 // A note on non-directory parents:
313 // We catch most unfixable tree invariant errors at update receipt time, 345 // We catch most unfixable tree invariant errors at update receipt time,
(...skipping 15 matching lines...) Expand all
329 Directory::ChildHandles handles; 361 Directory::ChildHandles handles;
330 trans->directory()->GetChildHandlesById(trans, id, &handles); 362 trans->directory()->GetChildHandlesById(trans, id, &handles);
331 if (!handles.empty()) { 363 if (!handles.empty()) {
332 // If we have still-existing children, then we need to deal with 364 // If we have still-existing children, then we need to deal with
333 // them before we can process this change. 365 // them before we can process this change.
334 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; 366 DVLOG(1) << "Not deleting directory; it's not empty " << *entry;
335 return CONFLICT; 367 return CONFLICT;
336 } 368 }
337 } 369 }
338 370
339 // Only apply updates that we can decrypt. If we can't decrypt the update, it 371 if (specifics.has_encrypted()) {
340 // is likely because the passphrase has not arrived yet. Because the 372 DVLOG(2) << "Received a decryptable "
341 // passphrase may not arrive within this GetUpdates, we can't just return
342 // conflict, else the syncer gets stuck. As such, we return
343 // CONFLICT_ENCRYPTION, which is treated as a non-blocking conflict. See the
344 // description in syncer_types.h.
345 if (specifics.has_encrypted() &&
346 !cryptographer->CanDecrypt(specifics.encrypted())) {
347 // We can't decrypt this node yet.
348 DVLOG(1) << "Received an undecryptable "
349 << syncable::ModelTypeToString(entry->GetServerModelType()) 373 << syncable::ModelTypeToString(entry->GetServerModelType())
350 << " update, returning encryption_conflict."; 374 << " update, applying normally.";
351 return CONFLICT_ENCRYPTION;
352 } else if (specifics.HasExtension(sync_pb::password) &&
353 entry->Get(UNIQUE_SERVER_TAG).empty()) {
354 // Passwords use their own legacy encryption scheme.
355 const sync_pb::PasswordSpecifics& password =
356 specifics.GetExtension(sync_pb::password);
357 if (!cryptographer->CanDecrypt(password.encrypted())) {
358 DVLOG(1) << "Received an undecryptable password update, returning "
359 << "encryption_conflict.";
360 return CONFLICT_ENCRYPTION;
361 }
362 } else { 375 } else {
363 if (specifics.has_encrypted()) { 376 DVLOG(2) << "Received an unencrypted "
364 DVLOG(2) << "Received a decryptable " 377 << syncable::ModelTypeToString(entry->GetServerModelType())
365 << syncable::ModelTypeToString(entry->GetServerModelType()) 378 << " update, applying normally.";
366 << " update, applying normally.";
367 } else {
368 DVLOG(2) << "Received an unencrypted "
369 << syncable::ModelTypeToString(entry->GetServerModelType())
370 << " update, applying normally.";
371 }
372 } 379 }
373 380
374 SyncerUtil::UpdateLocalDataFromServerData(trans, entry); 381 SyncerUtil::UpdateLocalDataFromServerData(trans, entry);
375 382
376 return SUCCESS; 383 return SUCCESS;
377 } 384 }
378 385
379 namespace { 386 namespace {
380 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally, 387 // Helper to synthesize a new-style sync_pb::EntitySpecifics for use locally,
381 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based 388 // when the server speaks only the old sync_pb::SyncEntity_BookmarkData-based
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 // commit we don't apply it as time differences may occur. 475 // commit we don't apply it as time differences may occur.
469 if (update.version() > target->Get(BASE_VERSION)) { 476 if (update.version() > target->Get(BASE_VERSION)) {
470 target->Put(IS_UNAPPLIED_UPDATE, true); 477 target->Put(IS_UNAPPLIED_UPDATE, true);
471 } 478 }
472 } 479 }
473 480
474 // Creates a new Entry iff no Entry exists with the given id. 481 // Creates a new Entry iff no Entry exists with the given id.
475 // static 482 // static
476 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans, 483 void SyncerUtil::CreateNewEntry(syncable::WriteTransaction *trans,
477 const syncable::Id& id) { 484 const syncable::Id& id) {
478 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, id); 485 syncable::MutableEntry entry(trans, GET_BY_ID, id);
479 if (!entry.good()) { 486 if (!entry.good()) {
480 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM, 487 syncable::MutableEntry new_entry(trans, syncable::CREATE_NEW_UPDATE_ITEM,
481 id); 488 id);
482 } 489 }
483 } 490 }
484 491
485 // static 492 // static
486 void SyncerUtil::SplitServerInformationIntoNewEntry( 493 void SyncerUtil::SplitServerInformationIntoNewEntry(
487 syncable::WriteTransaction* trans, 494 syncable::WriteTransaction* trans,
488 syncable::MutableEntry* entry) { 495 syncable::MutableEntry* entry) {
(...skipping 14 matching lines...) Expand all
503 // static 510 // static
504 void SyncerUtil::UpdateLocalDataFromServerData( 511 void SyncerUtil::UpdateLocalDataFromServerData(
505 syncable::WriteTransaction* trans, 512 syncable::WriteTransaction* trans,
506 syncable::MutableEntry* entry) { 513 syncable::MutableEntry* entry) {
507 DCHECK(!entry->Get(IS_UNSYNCED)); 514 DCHECK(!entry->Get(IS_UNSYNCED));
508 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE)); 515 DCHECK(entry->Get(IS_UNAPPLIED_UPDATE));
509 516
510 DVLOG(2) << "Updating entry : " << *entry; 517 DVLOG(2) << "Updating entry : " << *entry;
511 // Start by setting the properties that determine the model_type. 518 // Start by setting the properties that determine the model_type.
512 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS)); 519 entry->Put(SPECIFICS, entry->Get(SERVER_SPECIFICS));
520 // Clear the previous server specifics now that we're applying successfully.
521 entry->Put(BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
513 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR)); 522 entry->Put(IS_DIR, entry->Get(SERVER_IS_DIR));
514 // This strange dance around the IS_DEL flag avoids problems when setting 523 // This strange dance around the IS_DEL flag avoids problems when setting
515 // the name. 524 // the name.
516 // TODO(chron): Is this still an issue? Unit test this codepath. 525 // TODO(chron): Is this still an issue? Unit test this codepath.
517 if (entry->Get(SERVER_IS_DEL)) { 526 if (entry->Get(SERVER_IS_DEL)) {
518 entry->Put(IS_DEL, true); 527 entry->Put(IS_DEL, true);
519 } else { 528 } else {
520 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME)); 529 entry->Put(NON_UNIQUE_NAME, entry->Get(SERVER_NON_UNIQUE_NAME));
521 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID)); 530 entry->Put(PARENT_ID, entry->Get(SERVER_PARENT_ID));
522 CHECK(entry->Put(IS_DEL, false)); 531 CHECK(entry->Put(IS_DEL, false));
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
762 if (update.version() < target->Get(SERVER_VERSION)) { 771 if (update.version() < target->Get(SERVER_VERSION)) {
763 LOG(WARNING) << "Update older than current server version for " 772 LOG(WARNING) << "Update older than current server version for "
764 << *target << " Update:" 773 << *target << " Update:"
765 << SyncerProtoUtil::SyncEntityDebugString(update); 774 << SyncerProtoUtil::SyncEntityDebugString(update);
766 return VERIFY_SUCCESS; // Expected in new sync protocol. 775 return VERIFY_SUCCESS; // Expected in new sync protocol.
767 } 776 }
768 return VERIFY_UNDECIDED; 777 return VERIFY_UNDECIDED;
769 } 778 }
770 779
771 } // namespace browser_sync 780 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/syncer_unittest.cc ('k') | chrome/browser/sync/syncable/directory_backing_store.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698