OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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/syncer_util.h" | 5 #include "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> |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 | 177 |
178 // Just a quick sanity check. | 178 // Just a quick sanity check. |
179 DCHECK(!local_entry.GetId().ServerKnows()); | 179 DCHECK(!local_entry.GetId().ServerKnows()); |
180 | 180 |
181 DVLOG(1) << "Reuniting lost commit response IDs. server id: " | 181 DVLOG(1) << "Reuniting lost commit response IDs. server id: " |
182 << update_id << " local id: " << local_entry.GetId() | 182 << update_id << " local id: " << local_entry.GetId() |
183 << " new version: " << new_version; | 183 << " new version: " << new_version; |
184 | 184 |
185 return local_entry.GetId(); | 185 return local_entry.GetId(); |
186 } | 186 } |
| 187 } else if (update.has_server_defined_unique_tag() && |
| 188 !update.server_defined_unique_tag().empty()) { |
| 189 // The client creates type root folders with a local ID on demand when a |
| 190 // progress marker for the given type is initially set. |
| 191 // The server might also attempt to send a type root folder for the same |
| 192 // type (during the transition period until support for root folders is |
| 193 // removed for new client versions). |
| 194 // TODO(stanisc): crbug.com/438313: remove this once the transition to |
| 195 // implicit root folders on the server is done. |
| 196 syncable::Entry local_entry(trans, syncable::GET_BY_SERVER_TAG, |
| 197 update.server_defined_unique_tag()); |
| 198 if (local_entry.good() && !local_entry.GetId().ServerKnows()) { |
| 199 DCHECK(local_entry.GetId() != update_id); |
| 200 return local_entry.GetId(); |
| 201 } |
187 } | 202 } |
| 203 |
188 // Fallback: target an entry having the server ID, creating one if needed. | 204 // Fallback: target an entry having the server ID, creating one if needed. |
189 return update_id; | 205 return update_id; |
190 } | 206 } |
191 | 207 |
192 UpdateAttemptResponse AttemptToUpdateEntry( | 208 UpdateAttemptResponse AttemptToUpdateEntry( |
193 syncable::WriteTransaction* const trans, | 209 syncable::WriteTransaction* const trans, |
194 syncable::MutableEntry* const entry, | 210 syncable::MutableEntry* const entry, |
195 Cryptographer* cryptographer) { | 211 Cryptographer* cryptographer) { |
196 CHECK(entry->good()); | 212 CHECK(entry->good()); |
197 if (!entry->GetIsUnappliedUpdate()) | 213 if (!entry->GetIsUnappliedUpdate()) |
198 return SUCCESS; // No work to do. | 214 return SUCCESS; // No work to do. |
199 syncable::Id id = entry->GetId(); | 215 syncable::Id id = entry->GetId(); |
200 const sync_pb::EntitySpecifics& specifics = entry->GetServerSpecifics(); | 216 const sync_pb::EntitySpecifics& specifics = entry->GetServerSpecifics(); |
| 217 ModelType type = GetModelTypeFromSpecifics(specifics); |
201 | 218 |
202 // Only apply updates that we can decrypt. If we can't decrypt the update, it | 219 // Only apply updates that we can decrypt. If we can't decrypt the update, it |
203 // is likely because the passphrase has not arrived yet. Because the | 220 // is likely because the passphrase has not arrived yet. Because the |
204 // passphrase may not arrive within this GetUpdates, we can't just return | 221 // passphrase may not arrive within this GetUpdates, we can't just return |
205 // conflict, else we try to perform normal conflict resolution prematurely or | 222 // conflict, else we try to perform normal conflict resolution prematurely or |
206 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is | 223 // the syncer may get stuck. As such, we return CONFLICT_ENCRYPTION, which is |
207 // treated as an unresolvable conflict. See the description in syncer_types.h. | 224 // treated as an unresolvable conflict. See the description in syncer_types.h. |
208 // This prevents any unsynced changes from commiting and postpones conflict | 225 // This prevents any unsynced changes from commiting and postpones conflict |
209 // resolution until all data can be decrypted. | 226 // resolution until all data can be decrypted. |
210 if (specifics.has_encrypted() && | 227 if (specifics.has_encrypted() && |
211 !cryptographer->CanDecrypt(specifics.encrypted())) { | 228 !cryptographer->CanDecrypt(specifics.encrypted())) { |
212 // We can't decrypt this node yet. | 229 // We can't decrypt this node yet. |
213 DVLOG(1) << "Received an undecryptable " | 230 DVLOG(1) << "Received an undecryptable " |
214 << ModelTypeToString(entry->GetServerModelType()) | 231 << ModelTypeToString(entry->GetServerModelType()) |
215 << " update, returning conflict_encryption."; | 232 << " update, returning conflict_encryption."; |
216 return CONFLICT_ENCRYPTION; | 233 return CONFLICT_ENCRYPTION; |
217 } else if (specifics.has_password() && | 234 } else if (specifics.has_password() && |
218 entry->GetUniqueServerTag().empty()) { | 235 entry->GetUniqueServerTag().empty()) { |
219 // Passwords use their own legacy encryption scheme. | 236 // Passwords use their own legacy encryption scheme. |
220 const sync_pb::PasswordSpecifics& password = specifics.password(); | 237 const sync_pb::PasswordSpecifics& password = specifics.password(); |
221 if (!cryptographer->CanDecrypt(password.encrypted())) { | 238 if (!cryptographer->CanDecrypt(password.encrypted())) { |
222 DVLOG(1) << "Received an undecryptable password update, returning " | 239 DVLOG(1) << "Received an undecryptable password update, returning " |
223 << "conflict_encryption."; | 240 << "conflict_encryption."; |
224 return CONFLICT_ENCRYPTION; | 241 return CONFLICT_ENCRYPTION; |
225 } | 242 } |
226 } | 243 } |
227 | 244 |
228 if (!entry->GetServerIsDel()) { | 245 if (!entry->GetServerIsDel()) { |
229 syncable::Id new_parent = entry->GetServerParentId(); | 246 syncable::Id new_parent = entry->GetServerParentId(); |
230 Entry parent(trans, GET_BY_ID, new_parent); | 247 if (!new_parent.IsNull()) { |
231 // A note on non-directory parents: | 248 // Perform this step only if the parent is specified. |
232 // We catch most unfixable tree invariant errors at update receipt time, | 249 // The unset parent means that the implicit type root would be used. |
233 // however we deal with this case here because we may receive the child | 250 Entry parent(trans, GET_BY_ID, new_parent); |
234 // first then the illegal parent. Instead of dealing with it twice in | 251 // A note on non-directory parents: |
235 // different ways we deal with it once here to reduce the amount of code and | 252 // We catch most unfixable tree invariant errors at update receipt time, |
236 // potential errors. | 253 // however we deal with this case here because we may receive the child |
237 if (!parent.good() || parent.GetIsDel() || !parent.GetIsDir()) { | 254 // first then the illegal parent. Instead of dealing with it twice in |
238 DVLOG(1) << "Entry has bad parent, returning conflict_hierarchy."; | 255 // different ways we deal with it once here to reduce the amount of code |
239 return CONFLICT_HIERARCHY; | 256 // and potential errors. |
240 } | 257 if (!parent.good() || parent.GetIsDel() || !parent.GetIsDir()) { |
241 if (entry->GetParentId() != new_parent) { | 258 DVLOG(1) << "Entry has bad parent, returning conflict_hierarchy."; |
242 if (!entry->GetIsDel() && !IsLegalNewParent(trans, id, new_parent)) { | |
243 DVLOG(1) << "Not updating item " << id | |
244 << ", illegal new parent (would cause loop)."; | |
245 return CONFLICT_HIERARCHY; | 259 return CONFLICT_HIERARCHY; |
246 } | 260 } |
| 261 if (entry->GetParentId() != new_parent) { |
| 262 if (!entry->GetIsDel() && !IsLegalNewParent(trans, id, new_parent)) { |
| 263 DVLOG(1) << "Not updating item " << id |
| 264 << ", illegal new parent (would cause loop)."; |
| 265 return CONFLICT_HIERARCHY; |
| 266 } |
| 267 } |
| 268 } else { |
| 269 // new_parent is unset. |
| 270 DCHECK(!IsTypeWithServerGeneratedRoot(type)); |
247 } | 271 } |
248 } else if (entry->GetIsDir()) { | 272 } else if (entry->GetIsDir()) { |
249 Directory::Metahandles handles; | 273 Directory::Metahandles handles; |
250 trans->directory()->GetChildHandlesById(trans, id, &handles); | 274 trans->directory()->GetChildHandlesById(trans, id, &handles); |
251 if (!handles.empty()) { | 275 if (!handles.empty()) { |
252 // If we have still-existing children, then we need to deal with | 276 // If we have still-existing children, then we need to deal with |
253 // them before we can process this change. | 277 // them before we can process this change. |
254 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; | 278 DVLOG(1) << "Not deleting directory; it's not empty " << *entry; |
255 return CONFLICT_HIERARCHY; | 279 return CONFLICT_HIERARCHY; |
256 } | 280 } |
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 if (update.version() < target->GetServerVersion()) { | 669 if (update.version() < target->GetServerVersion()) { |
646 LOG(WARNING) << "Update older than current server version for " | 670 LOG(WARNING) << "Update older than current server version for " |
647 << *target << " Update:" | 671 << *target << " Update:" |
648 << SyncerProtoUtil::SyncEntityDebugString(update); | 672 << SyncerProtoUtil::SyncEntityDebugString(update); |
649 return VERIFY_SUCCESS; // Expected in new sync protocol. | 673 return VERIFY_SUCCESS; // Expected in new sync protocol. |
650 } | 674 } |
651 return VERIFY_UNDECIDED; | 675 return VERIFY_UNDECIDED; |
652 } | 676 } |
653 | 677 |
654 } // namespace syncer | 678 } // namespace syncer |
OLD | NEW |