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.
5 #include "sync/engine/process_updates_command.h"
9 #include "base/basictypes.h"
10 #include "base/location.h"
11 #include "sync/engine/syncer.h"
12 #include "sync/engine/syncer_proto_util.h"
13 #include "sync/engine/syncer_util.h"
14 #include "sync/sessions/sync_session.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/mutable_entry.h"
17 #include "sync/syncable/syncable_proto_util.h"
18 #include "sync/syncable/syncable_util.h"
19 #include "sync/syncable/write_transaction.h"
20 #include "sync/util/cryptographer.h"
26 using sessions::SyncSession
;
27 using sessions::StatusController
;
28 using sessions::UpdateProgress
;
30 ProcessUpdatesCommand::ProcessUpdatesCommand() {}
31 ProcessUpdatesCommand::~ProcessUpdatesCommand() {}
33 std::set
<ModelSafeGroup
> ProcessUpdatesCommand::GetGroupsToChange(
34 const sessions::SyncSession
& session
) const {
35 return session
.GetEnabledGroupsWithVerifiedUpdates();
38 SyncerError
ProcessUpdatesCommand::ModelChangingExecuteImpl(
39 SyncSession
* session
) {
40 syncable::Directory
* dir
= session
->context()->directory();
42 const sessions::UpdateProgress
* progress
=
43 session
->status_controller().update_progress();
45 return SYNCER_OK
; // Nothing to do.
47 syncable::WriteTransaction
trans(FROM_HERE
, syncable::SYNCER
, dir
);
48 vector
<sessions::VerifiedUpdate
>::const_iterator it
;
49 for (it
= progress
->VerifiedUpdatesBegin();
50 it
!= progress
->VerifiedUpdatesEnd();
52 const sync_pb::SyncEntity
& update
= it
->second
;
54 if (it
->first
!= VERIFY_SUCCESS
&& it
->first
!= VERIFY_UNDELETE
)
56 switch (ProcessUpdate(update
,
57 dir
->GetCryptographer(&trans
),
59 case SUCCESS_PROCESSED
:
68 StatusController
* status
= session
->mutable_status_controller();
69 status
->mutable_update_progress()->ClearVerifiedUpdates();
74 // Returns true if the entry is still ok to process.
75 bool ReverifyEntry(syncable::WriteTransaction
* trans
,
76 const sync_pb::SyncEntity
& entry
,
77 syncable::MutableEntry
* same_id
) {
79 const bool deleted
= entry
.has_deleted() && entry
.deleted();
80 const bool is_directory
= IsFolder(entry
);
81 const ModelType model_type
= GetModelType(entry
);
83 return VERIFY_SUCCESS
== VerifyUpdateConsistency(trans
,
92 // Process a single update. Will avoid touching global state.
93 ServerUpdateProcessingResult
ProcessUpdatesCommand::ProcessUpdate(
94 const sync_pb::SyncEntity
& update
,
95 const Cryptographer
* cryptographer
,
96 syncable::WriteTransaction
* const trans
) {
97 const syncable::Id
& server_id
= SyncableIdFromProto(update
.id_string());
98 const std::string name
= SyncerProtoUtil::NameFromSyncEntity(update
);
100 // Look to see if there's a local item that should recieve this update,
101 // maybe due to a duplicate client tag or a lost commit response.
102 syncable::Id local_id
= FindLocalIdToUpdate(trans
, update
);
104 // FindLocalEntryToUpdate has veto power.
105 if (local_id
.IsNull()) {
106 return SUCCESS_PROCESSED
; // The entry has become irrelevant.
109 CreateNewEntry(trans
, local_id
);
111 // We take a two step approach. First we store the entries data in the
112 // server fields of a local entry and then move the data to the local fields
113 syncable::MutableEntry
target_entry(trans
, syncable::GET_BY_ID
, local_id
);
115 // We need to run the Verify checks again; the world could have changed
116 // since VerifyUpdatesCommand.
117 if (!ReverifyEntry(trans
, update
, &target_entry
)) {
118 return SUCCESS_PROCESSED
; // The entry has become irrelevant.
121 // If we're repurposing an existing local entry with a new server ID,
122 // change the ID now, after we're sure that the update can succeed.
123 if (local_id
!= server_id
) {
124 DCHECK(!update
.deleted());
125 ChangeEntryIDAndUpdateChildren(trans
, &target_entry
, server_id
);
126 // When IDs change, versions become irrelevant. Forcing BASE_VERSION
127 // to zero would ensure that this update gets applied, but would indicate
128 // creation or undeletion if it were committed that way. Instead, prefer
129 // forcing BASE_VERSION to entry.version() while also forcing
130 // IS_UNAPPLIED_UPDATE to true. If the item is UNSYNCED, it's committable
131 // from the new state; it may commit before the conflict resolver gets
133 if (target_entry
.Get(syncable::IS_UNSYNCED
) ||
134 target_entry
.Get(syncable::BASE_VERSION
) > 0) {
135 // If either of these conditions are met, then we can expect valid client
136 // fields for this entry. When BASE_VERSION is positive, consistency is
137 // enforced on the client fields at update-application time. Otherwise,
138 // we leave the BASE_VERSION field alone; it'll get updated the first time
139 // we successfully apply this update.
140 target_entry
.Put(syncable::BASE_VERSION
, update
.version());
142 // Force application of this update, no matter what.
143 target_entry
.Put(syncable::IS_UNAPPLIED_UPDATE
, true);
146 // If this is a newly received undecryptable update, and the only thing that
147 // has changed are the specifics, store the original decryptable specifics,
148 // (on which any current or future local changes are based) before we
149 // overwrite SERVER_SPECIFICS.
150 // MTIME, CTIME, and NON_UNIQUE_NAME are not enforced.
151 if (!update
.deleted() && !target_entry
.Get(syncable::SERVER_IS_DEL
) &&
152 (SyncableIdFromProto(update
.parent_id_string()) ==
153 target_entry
.Get(syncable::SERVER_PARENT_ID
)) &&
154 (update
.position_in_parent() ==
155 target_entry
.Get(syncable::SERVER_POSITION_IN_PARENT
)) &&
156 update
.has_specifics() && update
.specifics().has_encrypted() &&
157 !cryptographer
->CanDecrypt(update
.specifics().encrypted())) {
158 sync_pb::EntitySpecifics prev_specifics
=
159 target_entry
.Get(syncable::SERVER_SPECIFICS
);
160 // We only store the old specifics if they were decryptable and applied and
161 // there is no BASE_SERVER_SPECIFICS already. Else do nothing.
162 if (!target_entry
.Get(syncable::IS_UNAPPLIED_UPDATE
) &&
163 !IsRealDataType(GetModelTypeFromSpecifics(
164 target_entry
.Get(syncable::BASE_SERVER_SPECIFICS
))) &&
165 (!prev_specifics
.has_encrypted() ||
166 cryptographer
->CanDecrypt(prev_specifics
.encrypted()))) {
167 DVLOG(2) << "Storing previous server specifcs: "
168 << prev_specifics
.SerializeAsString();
169 target_entry
.Put(syncable::BASE_SERVER_SPECIFICS
, prev_specifics
);
171 } else if (IsRealDataType(GetModelTypeFromSpecifics(
172 target_entry
.Get(syncable::BASE_SERVER_SPECIFICS
)))) {
173 // We have a BASE_SERVER_SPECIFICS, but a subsequent non-specifics-only
174 // change arrived. As a result, we can't use the specifics alone to detect
175 // changes, so we clear BASE_SERVER_SPECIFICS.
176 target_entry
.Put(syncable::BASE_SERVER_SPECIFICS
,
177 sync_pb::EntitySpecifics());
180 UpdateServerFieldsFromUpdate(&target_entry
, update
, name
);
182 return SUCCESS_PROCESSED
;
185 } // namespace syncer