OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |