| OLD | NEW | 
 | (Empty) | 
|    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 |  | 
|    3 // found in the LICENSE file. |  | 
|    4  |  | 
|    5 #include "sync/engine/verify_updates_command.h" |  | 
|    6  |  | 
|    7 #include <string> |  | 
|    8  |  | 
|    9 #include "base/location.h" |  | 
|   10 #include "sync/engine/syncer.h" |  | 
|   11 #include "sync/engine/syncer_proto_util.h" |  | 
|   12 #include "sync/engine/syncer_types.h" |  | 
|   13 #include "sync/engine/syncer_util.h" |  | 
|   14 #include "sync/internal_api/public/engine/model_safe_worker.h" |  | 
|   15 #include "sync/protocol/bookmark_specifics.pb.h" |  | 
|   16 #include "sync/protocol/sync.pb.h" |  | 
|   17 #include "sync/syncable/entry.h" |  | 
|   18 #include "sync/syncable/mutable_entry.h" |  | 
|   19 #include "sync/syncable/syncable_proto_util.h" |  | 
|   20 #include "sync/syncable/write_transaction.h" |  | 
|   21  |  | 
|   22 namespace syncer { |  | 
|   23  |  | 
|   24 using syncable::GET_BY_ID; |  | 
|   25 using syncable::SYNCER; |  | 
|   26 using syncable::WriteTransaction; |  | 
|   27  |  | 
|   28 namespace { |  | 
|   29  |  | 
|   30 // This function attempts to determine whether or not this update is genuinely |  | 
|   31 // new, or if it is a reflection of one of our own commits. |  | 
|   32 // |  | 
|   33 // There is a known inaccuracy in its implementation.  If this update ends up |  | 
|   34 // being applied to a local item with a different ID, we will count the change |  | 
|   35 // as being a non-reflection update.  Fortunately, the server usually updates |  | 
|   36 // our IDs correctly in its commit response, so a new ID during GetUpdate should |  | 
|   37 // be rare. |  | 
|   38 // |  | 
|   39 // The only secnarios I can think of where this might happen are: |  | 
|   40 // - We commit a  new item to the server, but we don't persist the |  | 
|   41 // server-returned new ID to the database before we shut down.  On the GetUpdate |  | 
|   42 // following the next restart, we will receive an update from the server that |  | 
|   43 // updates its local ID. |  | 
|   44 // - When two attempts to create an item with identical UNIQUE_CLIENT_TAG values |  | 
|   45 // collide at the server.  I have seen this in testing.  When it happens, the |  | 
|   46 // test server will send one of the clients a response to upate its local ID so |  | 
|   47 // that both clients will refer to the item using the same ID going forward.  In |  | 
|   48 // this case, we're right to assume that the update is not a reflection. |  | 
|   49 // |  | 
|   50 // For more information, see FindLocalIdToUpdate(). |  | 
|   51 bool UpdateContainsNewVersion(syncable::BaseTransaction *trans, |  | 
|   52                               const sync_pb::SyncEntity &update) { |  | 
|   53   int64 existing_version = -1; // The server always sends positive versions. |  | 
|   54   syncable::Entry existing_entry(trans, GET_BY_ID, |  | 
|   55                                  SyncableIdFromProto(update.id_string())); |  | 
|   56   if (existing_entry.good()) |  | 
|   57     existing_version = existing_entry.Get(syncable::BASE_VERSION); |  | 
|   58  |  | 
|   59   if (!existing_entry.good() && update.deleted()) { |  | 
|   60     // There are several possible explanations for this.  The most common cases |  | 
|   61     // will be first time sync and the redelivery of deletions we've already |  | 
|   62     // synced, accepted, and purged from our database.  In either case, the |  | 
|   63     // update is useless to us.  Let's count them all as "not new", even though |  | 
|   64     // that may not always be entirely accurate. |  | 
|   65     return false; |  | 
|   66   } |  | 
|   67  |  | 
|   68   if (existing_entry.good() && |  | 
|   69       !existing_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty() && |  | 
|   70       existing_entry.Get(syncable::IS_DEL) && |  | 
|   71       update.deleted()) { |  | 
|   72     // Unique client tags will have their version set to zero when they're |  | 
|   73     // deleted.  The usual version comparison logic won't be able to detect |  | 
|   74     // reflections of these items.  Instead, we assume any received tombstones |  | 
|   75     // are reflections.  That should be correct most of the time. |  | 
|   76     return false; |  | 
|   77   } |  | 
|   78  |  | 
|   79   return existing_version < update.version(); |  | 
|   80 } |  | 
|   81  |  | 
|   82 // In the event that IDs match, but tags differ AttemptReuniteClient tag |  | 
|   83 // will have refused to unify the update. |  | 
|   84 // We should not attempt to apply it at all since it violates consistency |  | 
|   85 // rules. |  | 
|   86 VerifyResult VerifyTagConsistency(const sync_pb::SyncEntity& entry, |  | 
|   87                                   const syncable::MutableEntry& same_id) { |  | 
|   88   if (entry.has_client_defined_unique_tag() && |  | 
|   89       entry.client_defined_unique_tag() != |  | 
|   90           same_id.Get(syncable::UNIQUE_CLIENT_TAG)) { |  | 
|   91     return VERIFY_FAIL; |  | 
|   92   } |  | 
|   93   return VERIFY_UNDECIDED; |  | 
|   94 } |  | 
|   95 }  // namespace |  | 
|   96  |  | 
|   97 VerifyUpdatesCommand::VerifyUpdatesCommand() {} |  | 
|   98 VerifyUpdatesCommand::~VerifyUpdatesCommand() {} |  | 
|   99  |  | 
|  100 std::set<ModelSafeGroup> VerifyUpdatesCommand::GetGroupsToChange( |  | 
|  101     const sessions::SyncSession& session) const { |  | 
|  102   std::set<ModelSafeGroup> groups_with_updates; |  | 
|  103  |  | 
|  104   const sync_pb::GetUpdatesResponse& updates = |  | 
|  105       session.status_controller().updates_response().get_updates(); |  | 
|  106   for (int i = 0; i < updates.entries().size(); i++) { |  | 
|  107     groups_with_updates.insert( |  | 
|  108         GetGroupForModelType(GetModelType(updates.entries(i)), |  | 
|  109                              session.routing_info())); |  | 
|  110   } |  | 
|  111  |  | 
|  112   return groups_with_updates; |  | 
|  113 } |  | 
|  114  |  | 
|  115 SyncerError VerifyUpdatesCommand::ModelChangingExecuteImpl( |  | 
|  116     sessions::SyncSession* session) { |  | 
|  117   DVLOG(1) << "Beginning Update Verification"; |  | 
|  118   syncable::Directory* dir = session->context()->directory(); |  | 
|  119   WriteTransaction trans(FROM_HERE, SYNCER, dir); |  | 
|  120   sessions::StatusController* status = session->mutable_status_controller(); |  | 
|  121   const sync_pb::GetUpdatesResponse& updates = |  | 
|  122       status->updates_response().get_updates(); |  | 
|  123   int update_count = updates.entries().size(); |  | 
|  124  |  | 
|  125   ModelTypeSet requested_types = GetRoutingInfoTypes( |  | 
|  126       session->routing_info()); |  | 
|  127  |  | 
|  128   DVLOG(1) << update_count << " entries to verify"; |  | 
|  129   for (int i = 0; i < update_count; i++) { |  | 
|  130     const sync_pb::SyncEntity& update = updates.entries(i); |  | 
|  131     ModelSafeGroup g = GetGroupForModelType(GetModelType(update), |  | 
|  132                                             session->routing_info()); |  | 
|  133     if (g != status->group_restriction()) |  | 
|  134       continue; |  | 
|  135  |  | 
|  136     VerifyUpdateResult result = VerifyUpdate(&trans, update, |  | 
|  137                                              requested_types, |  | 
|  138                                              session->routing_info()); |  | 
|  139     status->mutable_update_progress()->AddVerifyResult(result.value, update); |  | 
|  140     status->increment_num_updates_downloaded_by(1); |  | 
|  141     if (!UpdateContainsNewVersion(&trans, update)) |  | 
|  142       status->increment_num_reflected_updates_downloaded_by(1); |  | 
|  143     if (update.deleted()) |  | 
|  144       status->increment_num_tombstone_updates_downloaded_by(1); |  | 
|  145   } |  | 
|  146  |  | 
|  147   return SYNCER_OK; |  | 
|  148 } |  | 
|  149  |  | 
|  150 VerifyUpdatesCommand::VerifyUpdateResult VerifyUpdatesCommand::VerifyUpdate( |  | 
|  151     syncable::WriteTransaction* trans, const sync_pb::SyncEntity& entry, |  | 
|  152     ModelTypeSet requested_types, |  | 
|  153     const ModelSafeRoutingInfo& routes) { |  | 
|  154   syncable::Id id = SyncableIdFromProto(entry.id_string()); |  | 
|  155   VerifyUpdateResult result = {VERIFY_FAIL, GROUP_PASSIVE}; |  | 
|  156  |  | 
|  157   const bool deleted = entry.has_deleted() && entry.deleted(); |  | 
|  158   const bool is_directory = IsFolder(entry); |  | 
|  159   const ModelType model_type = GetModelType(entry); |  | 
|  160  |  | 
|  161   if (!id.ServerKnows()) { |  | 
|  162     LOG(ERROR) << "Illegal negative id in received updates"; |  | 
|  163     return result; |  | 
|  164   } |  | 
|  165   { |  | 
|  166     const std::string name = SyncerProtoUtil::NameFromSyncEntity(entry); |  | 
|  167     if (name.empty() && !deleted) { |  | 
|  168       LOG(ERROR) << "Zero length name in non-deleted update"; |  | 
|  169       return result; |  | 
|  170     } |  | 
|  171   } |  | 
|  172  |  | 
|  173   syncable::MutableEntry same_id(trans, GET_BY_ID, id); |  | 
|  174   result.value = VerifyNewEntry(entry, &same_id, deleted); |  | 
|  175  |  | 
|  176   ModelType placement_type = !deleted ? GetModelType(entry) |  | 
|  177       : same_id.good() ? same_id.GetModelType() : UNSPECIFIED; |  | 
|  178   result.placement = GetGroupForModelType(placement_type, routes); |  | 
|  179  |  | 
|  180   if (VERIFY_UNDECIDED == result.value) { |  | 
|  181     result.value = VerifyTagConsistency(entry, same_id); |  | 
|  182   } |  | 
|  183  |  | 
|  184   if (VERIFY_UNDECIDED == result.value) { |  | 
|  185     if (deleted) { |  | 
|  186       // For deletes the server could send tombostones for items that |  | 
|  187       // the client did not request. If so ignore those items. |  | 
|  188       if (IsRealDataType(placement_type) && |  | 
|  189           !requested_types.Has(placement_type)) { |  | 
|  190         result.value = VERIFY_SKIP; |  | 
|  191       } else { |  | 
|  192         result.value = VERIFY_SUCCESS; |  | 
|  193       } |  | 
|  194     } |  | 
|  195   } |  | 
|  196  |  | 
|  197   // If we have an existing entry, we check here for updates that break |  | 
|  198   // consistency rules. |  | 
|  199   if (VERIFY_UNDECIDED == result.value) { |  | 
|  200     result.value = VerifyUpdateConsistency(trans, entry, &same_id, |  | 
|  201         deleted, is_directory, model_type); |  | 
|  202   } |  | 
|  203  |  | 
|  204   if (VERIFY_UNDECIDED == result.value) |  | 
|  205     result.value = VERIFY_SUCCESS;  // No news is good news. |  | 
|  206  |  | 
|  207   return result;  // This might be VERIFY_SUCCESS as well |  | 
|  208 } |  | 
|  209  |  | 
|  210 }  // namespace syncer |  | 
| OLD | NEW |