OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "sync/engine/process_updates_util.h" | 5 #include "components/sync/engine_impl/process_updates_util.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <string> | 10 #include <string> |
11 | 11 |
12 #include "base/location.h" | 12 #include "base/location.h" |
13 #include "base/metrics/sparse_histogram.h" | 13 #include "base/metrics/sparse_histogram.h" |
14 #include "sync/engine/syncer_proto_util.h" | 14 #include "components/sync/base/cryptographer.h" |
15 #include "sync/engine/syncer_types.h" | 15 #include "components/sync/base/data_type_histogram.h" |
16 #include "sync/engine/syncer_util.h" | 16 #include "components/sync/engine_impl/syncer_proto_util.h" |
17 #include "sync/internal_api/public/sessions/update_counters.h" | 17 #include "components/sync/engine_impl/syncer_types.h" |
18 #include "sync/syncable/directory.h" | 18 #include "components/sync/engine_impl/syncer_util.h" |
19 #include "sync/syncable/model_neutral_mutable_entry.h" | 19 #include "components/sync/sessions/update_counters.h" |
20 #include "sync/syncable/syncable_model_neutral_write_transaction.h" | 20 #include "components/sync/syncable/directory.h" |
21 #include "sync/syncable/syncable_proto_util.h" | 21 #include "components/sync/syncable/model_neutral_mutable_entry.h" |
22 #include "sync/syncable/syncable_util.h" | 22 #include "components/sync/syncable/syncable_model_neutral_write_transaction.h" |
23 #include "sync/util/cryptographer.h" | 23 #include "components/sync/syncable/syncable_proto_util.h" |
24 #include "sync/util/data_type_histogram.h" | 24 #include "components/sync/syncable/syncable_util.h" |
25 | 25 |
26 namespace syncer { | 26 namespace syncer { |
27 | 27 |
28 using sessions::StatusController; | 28 using sessions::StatusController; |
29 | 29 |
30 using syncable::GET_BY_ID; | 30 using syncable::GET_BY_ID; |
31 | 31 |
32 namespace { | 32 namespace { |
33 | 33 |
34 // This function attempts to determine whether or not this update is genuinely | 34 // This function attempts to determine whether or not this update is genuinely |
(...skipping 10 matching lines...) Expand all Loading... |
45 // server-returned new ID to the database before we shut down. On the GetUpdate | 45 // server-returned new ID to the database before we shut down. On the GetUpdate |
46 // following the next restart, we will receive an update from the server that | 46 // following the next restart, we will receive an update from the server that |
47 // updates its local ID. | 47 // updates its local ID. |
48 // - When two attempts to create an item with identical UNIQUE_CLIENT_TAG values | 48 // - When two attempts to create an item with identical UNIQUE_CLIENT_TAG values |
49 // collide at the server. I have seen this in testing. When it happens, the | 49 // collide at the server. I have seen this in testing. When it happens, the |
50 // test server will send one of the clients a response to upate its local ID so | 50 // test server will send one of the clients a response to upate its local ID so |
51 // that both clients will refer to the item using the same ID going forward. In | 51 // that both clients will refer to the item using the same ID going forward. In |
52 // this case, we're right to assume that the update is not a reflection. | 52 // this case, we're right to assume that the update is not a reflection. |
53 // | 53 // |
54 // For more information, see FindLocalIdToUpdate(). | 54 // For more information, see FindLocalIdToUpdate(). |
55 bool UpdateContainsNewVersion(syncable::BaseTransaction *trans, | 55 bool UpdateContainsNewVersion(syncable::BaseTransaction* trans, |
56 const sync_pb::SyncEntity &update) { | 56 const sync_pb::SyncEntity& update) { |
57 int64_t existing_version = -1; // The server always sends positive versions. | 57 int64_t existing_version = -1; // The server always sends positive versions. |
58 syncable::Entry existing_entry(trans, GET_BY_ID, | 58 syncable::Entry existing_entry(trans, GET_BY_ID, |
59 SyncableIdFromProto(update.id_string())); | 59 SyncableIdFromProto(update.id_string())); |
60 if (existing_entry.good()) | 60 if (existing_entry.good()) |
61 existing_version = existing_entry.GetBaseVersion(); | 61 existing_version = existing_entry.GetBaseVersion(); |
62 | 62 |
63 if (!existing_entry.good() && update.deleted()) { | 63 if (!existing_entry.good() && update.deleted()) { |
64 // There are several possible explanations for this. The most common cases | 64 // There are several possible explanations for this. The most common cases |
65 // will be first time sync and the redelivery of deletions we've already | 65 // will be first time sync and the redelivery of deletions we've already |
66 // synced, accepted, and purged from our database. In either case, the | 66 // synced, accepted, and purged from our database. In either case, the |
67 // update is useless to us. Let's count them all as "not new", even though | 67 // update is useless to us. Let's count them all as "not new", even though |
68 // that may not always be entirely accurate. | 68 // that may not always be entirely accurate. |
69 return false; | 69 return false; |
70 } | 70 } |
71 | 71 |
72 if (existing_entry.good() && | 72 if (existing_entry.good() && !existing_entry.GetUniqueClientTag().empty() && |
73 !existing_entry.GetUniqueClientTag().empty() && | 73 existing_entry.GetIsDel() && update.deleted()) { |
74 existing_entry.GetIsDel() && | |
75 update.deleted()) { | |
76 // Unique client tags will have their version set to zero when they're | 74 // Unique client tags will have their version set to zero when they're |
77 // deleted. The usual version comparison logic won't be able to detect | 75 // deleted. The usual version comparison logic won't be able to detect |
78 // reflections of these items. Instead, we assume any received tombstones | 76 // reflections of these items. Instead, we assume any received tombstones |
79 // are reflections. That should be correct most of the time. | 77 // are reflections. That should be correct most of the time. |
80 return false; | 78 return false; |
81 } | 79 } |
82 | 80 |
83 return existing_version < update.version(); | 81 return existing_version < update.version(); |
84 } | 82 } |
85 | 83 |
86 // In the event that IDs match, but tags differ AttemptReuniteClient tag | 84 // In the event that IDs match, but tags differ AttemptReuniteClient tag |
87 // will have refused to unify the update. | 85 // will have refused to unify the update. |
88 // We should not attempt to apply it at all since it violates consistency | 86 // We should not attempt to apply it at all since it violates consistency |
89 // rules. | 87 // rules. |
90 VerifyResult VerifyTagConsistency( | 88 VerifyResult VerifyTagConsistency( |
91 const sync_pb::SyncEntity& entry, | 89 const sync_pb::SyncEntity& entry, |
92 const syncable::ModelNeutralMutableEntry& same_id) { | 90 const syncable::ModelNeutralMutableEntry& same_id) { |
93 if (entry.has_client_defined_unique_tag() && | 91 if (entry.has_client_defined_unique_tag() && |
94 entry.client_defined_unique_tag() != | 92 entry.client_defined_unique_tag() != same_id.GetUniqueClientTag()) { |
95 same_id.GetUniqueClientTag()) { | |
96 return VERIFY_FAIL; | 93 return VERIFY_FAIL; |
97 } | 94 } |
98 return VERIFY_UNDECIDED; | 95 return VERIFY_UNDECIDED; |
99 } | 96 } |
100 | 97 |
101 // Checks whether or not an update is fit for processing. | 98 // Checks whether or not an update is fit for processing. |
102 // | 99 // |
103 // The answer may be "no" if the update appears invalid, or it's not releveant | 100 // The answer may be "no" if the update appears invalid, or it's not releveant |
104 // (ie. a delete for an item we've never heard of), or other reasons. | 101 // (ie. a delete for an item we've never heard of), or other reasons. |
105 VerifyResult VerifyUpdate( | 102 VerifyResult VerifyUpdate(syncable::ModelNeutralWriteTransaction* trans, |
106 syncable::ModelNeutralWriteTransaction* trans, | 103 const sync_pb::SyncEntity& entry, |
107 const sync_pb::SyncEntity& entry, | 104 ModelType requested_type) { |
108 ModelType requested_type) { | |
109 syncable::Id id = SyncableIdFromProto(entry.id_string()); | 105 syncable::Id id = SyncableIdFromProto(entry.id_string()); |
110 VerifyResult result = VERIFY_FAIL; | 106 VerifyResult result = VERIFY_FAIL; |
111 | 107 |
112 const bool deleted = entry.has_deleted() && entry.deleted(); | 108 const bool deleted = entry.has_deleted() && entry.deleted(); |
113 const bool is_directory = IsFolder(entry); | 109 const bool is_directory = IsFolder(entry); |
114 const ModelType model_type = GetModelType(entry); | 110 const ModelType model_type = GetModelType(entry); |
115 | 111 |
116 if (!id.ServerKnows()) { | 112 if (!id.ServerKnows()) { |
117 LOG(ERROR) << "Illegal negative id in received updates"; | 113 LOG(ERROR) << "Illegal negative id in received updates"; |
118 return result; | 114 return result; |
119 } | 115 } |
120 { | 116 { |
121 const std::string name = SyncerProtoUtil::NameFromSyncEntity(entry); | 117 const std::string name = SyncerProtoUtil::NameFromSyncEntity(entry); |
122 if (name.empty() && !deleted) { | 118 if (name.empty() && !deleted) { |
123 LOG(ERROR) << "Zero length name in non-deleted update"; | 119 LOG(ERROR) << "Zero length name in non-deleted update"; |
124 return result; | 120 return result; |
125 } | 121 } |
126 } | 122 } |
127 | 123 |
128 syncable::ModelNeutralMutableEntry same_id(trans, GET_BY_ID, id); | 124 syncable::ModelNeutralMutableEntry same_id(trans, GET_BY_ID, id); |
129 result = VerifyNewEntry(entry, &same_id, deleted); | 125 result = VerifyNewEntry(entry, &same_id, deleted); |
130 | 126 |
131 ModelType placement_type = !deleted ? GetModelType(entry) | 127 ModelType placement_type = |
132 : same_id.good() ? same_id.GetModelType() : UNSPECIFIED; | 128 !deleted ? GetModelType(entry) : same_id.good() ? same_id.GetModelType() |
| 129 : UNSPECIFIED; |
133 | 130 |
134 if (VERIFY_UNDECIDED == result) { | 131 if (VERIFY_UNDECIDED == result) { |
135 result = VerifyTagConsistency(entry, same_id); | 132 result = VerifyTagConsistency(entry, same_id); |
136 } | 133 } |
137 | 134 |
138 if (VERIFY_UNDECIDED == result) { | 135 if (VERIFY_UNDECIDED == result) { |
139 if (deleted) { | 136 if (deleted) { |
140 // For deletes the server could send tombostones for items that | 137 // For deletes the server could send tombostones for items that |
141 // the client did not request. If so ignore those items. | 138 // the client did not request. If so ignore those items. |
142 if (IsRealDataType(placement_type) && requested_type != placement_type) { | 139 if (IsRealDataType(placement_type) && requested_type != placement_type) { |
143 result = VERIFY_SKIP; | 140 result = VERIFY_SKIP; |
144 } else { | 141 } else { |
145 result = VERIFY_SUCCESS; | 142 result = VERIFY_SUCCESS; |
146 } | 143 } |
147 } | 144 } |
148 } | 145 } |
149 | 146 |
150 // If we have an existing entry, we check here for updates that break | 147 // If we have an existing entry, we check here for updates that break |
151 // consistency rules. | 148 // consistency rules. |
152 if (VERIFY_UNDECIDED == result) { | 149 if (VERIFY_UNDECIDED == result) { |
153 result = VerifyUpdateConsistency(trans, entry, deleted, | 150 result = VerifyUpdateConsistency(trans, entry, deleted, is_directory, |
154 is_directory, model_type, &same_id); | 151 model_type, &same_id); |
155 } | 152 } |
156 | 153 |
157 if (VERIFY_UNDECIDED == result) | 154 if (VERIFY_UNDECIDED == result) |
158 result = VERIFY_SUCCESS; // No news is good news. | 155 result = VERIFY_SUCCESS; // No news is good news. |
159 | 156 |
160 return result; // This might be VERIFY_SUCCESS as well | 157 return result; // This might be VERIFY_SUCCESS as well |
161 } | 158 } |
162 | 159 |
163 // Returns true if the entry is still ok to process. | 160 // Returns true if the entry is still ok to process. |
164 bool ReverifyEntry(syncable::ModelNeutralWriteTransaction* trans, | 161 bool ReverifyEntry(syncable::ModelNeutralWriteTransaction* trans, |
165 const sync_pb::SyncEntity& entry, | 162 const sync_pb::SyncEntity& entry, |
166 syncable::ModelNeutralMutableEntry* same_id) { | 163 syncable::ModelNeutralMutableEntry* same_id) { |
167 const bool deleted = entry.has_deleted() && entry.deleted(); | 164 const bool deleted = entry.has_deleted() && entry.deleted(); |
168 const bool is_directory = IsFolder(entry); | 165 const bool is_directory = IsFolder(entry); |
169 const ModelType model_type = GetModelType(entry); | 166 const ModelType model_type = GetModelType(entry); |
170 | 167 |
171 return VERIFY_SUCCESS == VerifyUpdateConsistency(trans, | 168 return VERIFY_SUCCESS == VerifyUpdateConsistency(trans, entry, deleted, |
172 entry, | 169 is_directory, model_type, |
173 deleted, | |
174 is_directory, | |
175 model_type, | |
176 same_id); | 170 same_id); |
177 } | 171 } |
178 | 172 |
179 // Process a single update. Will avoid touching global state. | 173 // Process a single update. Will avoid touching global state. |
180 // | 174 // |
181 // If the update passes a series of checks, this function will copy | 175 // If the update passes a series of checks, this function will copy |
182 // the SyncEntity's data into the SERVER side of the syncable::Directory. | 176 // the SyncEntity's data into the SERVER side of the syncable::Directory. |
183 void ProcessUpdate( | 177 void ProcessUpdate(const sync_pb::SyncEntity& update, |
184 const sync_pb::SyncEntity& update, | 178 const Cryptographer* cryptographer, |
185 const Cryptographer* cryptographer, | 179 syncable::ModelNeutralWriteTransaction* const trans) { |
186 syncable::ModelNeutralWriteTransaction* const trans) { | |
187 const syncable::Id& server_id = SyncableIdFromProto(update.id_string()); | 180 const syncable::Id& server_id = SyncableIdFromProto(update.id_string()); |
188 const std::string name = SyncerProtoUtil::NameFromSyncEntity(update); | 181 const std::string name = SyncerProtoUtil::NameFromSyncEntity(update); |
189 | 182 |
190 // Look to see if there's a local item that should recieve this update, | 183 // Look to see if there's a local item that should recieve this update, |
191 // maybe due to a duplicate client tag or a lost commit response. | 184 // maybe due to a duplicate client tag or a lost commit response. |
192 syncable::Id local_id = FindLocalIdToUpdate(trans, update); | 185 syncable::Id local_id = FindLocalIdToUpdate(trans, update); |
193 | 186 |
194 // FindLocalEntryToUpdate has veto power. | 187 // FindLocalEntryToUpdate has veto power. |
195 if (local_id.IsNull()) { | 188 if (local_id.IsNull()) { |
196 return; // The entry has become irrelevant. | 189 return; // The entry has become irrelevant. |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 // If this is a newly received undecryptable update, and the only thing that | 228 // If this is a newly received undecryptable update, and the only thing that |
236 // has changed are the specifics, store the original decryptable specifics, | 229 // has changed are the specifics, store the original decryptable specifics, |
237 // (on which any current or future local changes are based) before we | 230 // (on which any current or future local changes are based) before we |
238 // overwrite SERVER_SPECIFICS. | 231 // overwrite SERVER_SPECIFICS. |
239 // MTIME, CTIME, and NON_UNIQUE_NAME are not enforced. | 232 // MTIME, CTIME, and NON_UNIQUE_NAME are not enforced. |
240 | 233 |
241 bool position_matches = false; | 234 bool position_matches = false; |
242 if (target_entry.ShouldMaintainPosition() && !update.deleted()) { | 235 if (target_entry.ShouldMaintainPosition() && !update.deleted()) { |
243 std::string update_tag = GetUniqueBookmarkTagFromUpdate(update); | 236 std::string update_tag = GetUniqueBookmarkTagFromUpdate(update); |
244 if (UniquePosition::IsValidSuffix(update_tag)) { | 237 if (UniquePosition::IsValidSuffix(update_tag)) { |
245 position_matches = GetUpdatePosition(update, update_tag).Equals( | 238 position_matches = GetUpdatePosition(update, update_tag) |
246 target_entry.GetServerUniquePosition()); | 239 .Equals(target_entry.GetServerUniquePosition()); |
247 } else { | 240 } else { |
248 NOTREACHED(); | 241 NOTREACHED(); |
249 } | 242 } |
250 } else { | 243 } else { |
251 // If this item doesn't care about positions, then set this flag to true. | 244 // If this item doesn't care about positions, then set this flag to true. |
252 position_matches = true; | 245 position_matches = true; |
253 } | 246 } |
254 | 247 |
255 if (!update.deleted() && !target_entry.GetServerIsDel() && | 248 if (!update.deleted() && !target_entry.GetServerIsDel() && |
256 (SyncableIdFromProto(update.parent_id_string()) == | 249 (SyncableIdFromProto(update.parent_id_string()) == |
257 target_entry.GetServerParentId()) && | 250 target_entry.GetServerParentId()) && |
258 position_matches && | 251 position_matches && update.has_specifics() && |
259 update.has_specifics() && update.specifics().has_encrypted() && | 252 update.specifics().has_encrypted() && |
260 !cryptographer->CanDecrypt(update.specifics().encrypted())) { | 253 !cryptographer->CanDecrypt(update.specifics().encrypted())) { |
261 sync_pb::EntitySpecifics prev_specifics = | 254 sync_pb::EntitySpecifics prev_specifics = target_entry.GetServerSpecifics(); |
262 target_entry.GetServerSpecifics(); | |
263 // We only store the old specifics if they were decryptable and applied and | 255 // We only store the old specifics if they were decryptable and applied and |
264 // there is no BASE_SERVER_SPECIFICS already. Else do nothing. | 256 // there is no BASE_SERVER_SPECIFICS already. Else do nothing. |
265 if (!target_entry.GetIsUnappliedUpdate() && | 257 if (!target_entry.GetIsUnappliedUpdate() && |
266 !IsRealDataType(GetModelTypeFromSpecifics( | 258 !IsRealDataType( |
267 target_entry.GetBaseServerSpecifics())) && | 259 GetModelTypeFromSpecifics(target_entry.GetBaseServerSpecifics())) && |
268 (!prev_specifics.has_encrypted() || | 260 (!prev_specifics.has_encrypted() || |
269 cryptographer->CanDecrypt(prev_specifics.encrypted()))) { | 261 cryptographer->CanDecrypt(prev_specifics.encrypted()))) { |
270 DVLOG(2) << "Storing previous server specifcs: " | 262 DVLOG(2) << "Storing previous server specifcs: " |
271 << prev_specifics.SerializeAsString(); | 263 << prev_specifics.SerializeAsString(); |
272 target_entry.PutBaseServerSpecifics(prev_specifics); | 264 target_entry.PutBaseServerSpecifics(prev_specifics); |
273 } | 265 } |
274 } else if (IsRealDataType(GetModelTypeFromSpecifics( | 266 } else if (IsRealDataType(GetModelTypeFromSpecifics( |
275 target_entry.GetBaseServerSpecifics()))) { | 267 target_entry.GetBaseServerSpecifics()))) { |
276 // We have a BASE_SERVER_SPECIFICS, but a subsequent non-specifics-only | 268 // We have a BASE_SERVER_SPECIFICS, but a subsequent non-specifics-only |
277 // change arrived. As a result, we can't use the specifics alone to detect | 269 // change arrived. As a result, we can't use the specifics alone to detect |
278 // changes, so we clear BASE_SERVER_SPECIFICS. | 270 // changes, so we clear BASE_SERVER_SPECIFICS. |
279 target_entry.PutBaseServerSpecifics( | 271 target_entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); |
280 sync_pb::EntitySpecifics()); | |
281 } | 272 } |
282 | 273 |
283 UpdateServerFieldsFromUpdate(&target_entry, update, name); | 274 UpdateServerFieldsFromUpdate(&target_entry, update, name); |
284 | 275 |
285 return; | 276 return; |
286 } | 277 } |
287 | 278 |
288 } // namespace | 279 } // namespace |
289 | 280 |
290 void ProcessDownloadedUpdates( | 281 void ProcessDownloadedUpdates(syncable::Directory* dir, |
291 syncable::Directory* dir, | 282 syncable::ModelNeutralWriteTransaction* trans, |
292 syncable::ModelNeutralWriteTransaction* trans, | 283 ModelType type, |
293 ModelType type, | 284 const SyncEntityList& applicable_updates, |
294 const SyncEntityList& applicable_updates, | 285 sessions::StatusController* status, |
295 sessions::StatusController* status, | 286 UpdateCounters* counters) { |
296 UpdateCounters* counters) { | |
297 for (SyncEntityList::const_iterator update_it = applicable_updates.begin(); | 287 for (SyncEntityList::const_iterator update_it = applicable_updates.begin(); |
298 update_it != applicable_updates.end(); ++update_it) { | 288 update_it != applicable_updates.end(); ++update_it) { |
299 DCHECK_EQ(type, GetModelType(**update_it)); | 289 DCHECK_EQ(type, GetModelType(**update_it)); |
300 if (!UpdateContainsNewVersion(trans, **update_it)) { | 290 if (!UpdateContainsNewVersion(trans, **update_it)) { |
301 status->increment_num_reflected_updates_downloaded_by(1); | 291 status->increment_num_reflected_updates_downloaded_by(1); |
302 counters->num_reflected_updates_received++; | 292 counters->num_reflected_updates_received++; |
303 } | 293 } |
304 if ((*update_it)->deleted()) { | 294 if ((*update_it)->deleted()) { |
305 status->increment_num_tombstone_updates_downloaded_by(1); | 295 status->increment_num_tombstone_updates_downloaded_by(1); |
306 counters->num_tombstone_updates_received++; | 296 counters->num_tombstone_updates_received++; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 | 328 |
339 // Mark entry as unapplied update first to ensure journaling the deletion. | 329 // Mark entry as unapplied update first to ensure journaling the deletion. |
340 entry.PutIsUnappliedUpdate(true); | 330 entry.PutIsUnappliedUpdate(true); |
341 // Mark entry as deleted by server. | 331 // Mark entry as deleted by server. |
342 entry.PutServerIsDel(true); | 332 entry.PutServerIsDel(true); |
343 entry.PutServerVersion(version_watermark); | 333 entry.PutServerVersion(version_watermark); |
344 } | 334 } |
345 } | 335 } |
346 | 336 |
347 } // namespace syncer | 337 } // namespace syncer |
OLD | NEW |