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