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
3 // found in the LICENSE file.
4
5 #include "sync/syncable/model_neutral_mutable_entry.h"
6
7 #include <string>
8
9 #include "sync/internal_api/public/base/unique_position.h"
10 #include "sync/syncable/directory.h"
11 #include "sync/syncable/scoped_kernel_lock.h"
12 #include "sync/syncable/syncable_changes_version.h"
13 #include "sync/syncable/syncable_util.h"
14 #include "sync/syncable/syncable_write_transaction.h"
15
16 using std::string;
17
18 namespace syncer {
19
20 namespace syncable {
21
ModelNeutralMutableEntry(BaseWriteTransaction * trans,CreateNewUpdateItem,const Id & id)22 ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans,
23 CreateNewUpdateItem,
24 const Id& id)
25 : Entry(trans), base_write_transaction_(trans) {
26 Entry same_id(trans, GET_BY_ID, id);
27 kernel_ = NULL;
28 if (same_id.good()) {
29 return; // already have an item with this ID.
30 }
31 scoped_ptr<EntryKernel> kernel(new EntryKernel());
32
33 kernel->put(ID, id);
34 kernel->put(META_HANDLE, trans->directory()->NextMetahandle());
35 kernel->mark_dirty(&trans->directory()->kernel_->dirty_metahandles);
36 kernel->put(IS_DEL, true);
37 // We match the database defaults here
38 kernel->put(BASE_VERSION, CHANGES_VERSION);
39 if (!trans->directory()->InsertEntry(trans, kernel.get())) {
40 return; // Failed inserting.
41 }
42 trans->TrackChangesTo(kernel.get());
43
44 kernel_ = kernel.release();
45 }
46
ModelNeutralMutableEntry(BaseWriteTransaction * trans,GetById,const Id & id)47 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
48 BaseWriteTransaction* trans, GetById, const Id& id)
49 : Entry(trans, GET_BY_ID, id), base_write_transaction_(trans) {
50 }
51
ModelNeutralMutableEntry(BaseWriteTransaction * trans,GetByHandle,int64 metahandle)52 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
53 BaseWriteTransaction* trans, GetByHandle, int64 metahandle)
54 : Entry(trans, GET_BY_HANDLE, metahandle), base_write_transaction_(trans) {
55 }
56
ModelNeutralMutableEntry(BaseWriteTransaction * trans,GetByClientTag,const std::string & tag)57 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
58 BaseWriteTransaction* trans, GetByClientTag, const std::string& tag)
59 : Entry(trans, GET_BY_CLIENT_TAG, tag), base_write_transaction_(trans) {
60 }
61
ModelNeutralMutableEntry(BaseWriteTransaction * trans,GetByServerTag,const string & tag)62 ModelNeutralMutableEntry::ModelNeutralMutableEntry(
63 BaseWriteTransaction* trans, GetByServerTag, const string& tag)
64 : Entry(trans, GET_BY_SERVER_TAG, tag), base_write_transaction_(trans) {
65 }
66
PutBaseVersion(int64 value)67 void ModelNeutralMutableEntry::PutBaseVersion(int64 value) {
68 DCHECK(kernel_);
69 base_write_transaction_->TrackChangesTo(kernel_);
70 if (kernel_->ref(BASE_VERSION) != value) {
71 kernel_->put(BASE_VERSION, value);
72 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
73 }
74 }
75
PutServerVersion(int64 value)76 void ModelNeutralMutableEntry::PutServerVersion(int64 value) {
77 DCHECK(kernel_);
78 base_write_transaction_->TrackChangesTo(kernel_);
79 if (kernel_->ref(SERVER_VERSION) != value) {
80 ScopedKernelLock lock(dir());
81 kernel_->put(SERVER_VERSION, value);
82 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
83 }
84 }
85
PutServerMtime(base::Time value)86 void ModelNeutralMutableEntry::PutServerMtime(base::Time value) {
87 DCHECK(kernel_);
88 base_write_transaction_->TrackChangesTo(kernel_);
89 if (kernel_->ref(SERVER_MTIME) != value) {
90 kernel_->put(SERVER_MTIME, value);
91 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
92 }
93 }
94
PutServerCtime(base::Time value)95 void ModelNeutralMutableEntry::PutServerCtime(base::Time value) {
96 DCHECK(kernel_);
97 base_write_transaction_->TrackChangesTo(kernel_);
98 if (kernel_->ref(SERVER_CTIME) != value) {
99 kernel_->put(SERVER_CTIME, value);
100 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
101 }
102 }
103
PutId(const Id & value)104 bool ModelNeutralMutableEntry::PutId(const Id& value) {
105 DCHECK(kernel_);
106 base_write_transaction_->TrackChangesTo(kernel_);
107 if (kernel_->ref(ID) != value) {
108 if (!dir()->ReindexId(base_write_transaction(), kernel_, value))
109 return false;
110 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
111 }
112 return true;
113 }
114
PutServerParentId(const Id & value)115 void ModelNeutralMutableEntry::PutServerParentId(const Id& value) {
116 DCHECK(kernel_);
117 base_write_transaction_->TrackChangesTo(kernel_);
118
119 if (kernel_->ref(SERVER_PARENT_ID) != value) {
120 kernel_->put(SERVER_PARENT_ID, value);
121 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
122 }
123 }
124
PutIsUnsynced(bool value)125 bool ModelNeutralMutableEntry::PutIsUnsynced(bool value) {
126 DCHECK(kernel_);
127 base_write_transaction_->TrackChangesTo(kernel_);
128 if (kernel_->ref(IS_UNSYNCED) != value) {
129 MetahandleSet* index = &dir()->kernel_->unsynced_metahandles;
130
131 ScopedKernelLock lock(dir());
132 if (value) {
133 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
134 FROM_HERE,
135 "Could not insert",
136 base_write_transaction())) {
137 return false;
138 }
139 } else {
140 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
141 FROM_HERE,
142 "Entry Not succesfully erased",
143 base_write_transaction())) {
144 return false;
145 }
146 }
147 kernel_->put(IS_UNSYNCED, value);
148 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
149 }
150 return true;
151 }
152
PutIsUnappliedUpdate(bool value)153 bool ModelNeutralMutableEntry::PutIsUnappliedUpdate(bool value) {
154 DCHECK(kernel_);
155 base_write_transaction_->TrackChangesTo(kernel_);
156 if (kernel_->ref(IS_UNAPPLIED_UPDATE) != value) {
157 // Use kernel_->GetServerModelType() instead of
158 // GetServerModelType() as we may trigger some DCHECKs in the
159 // latter.
160 MetahandleSet* index = &dir()->kernel_->unapplied_update_metahandles[
161 kernel_->GetServerModelType()];
162
163 ScopedKernelLock lock(dir());
164 if (value) {
165 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
166 FROM_HERE,
167 "Could not insert",
168 base_write_transaction())) {
169 return false;
170 }
171 } else {
172 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
173 FROM_HERE,
174 "Entry Not succesfully erased",
175 base_write_transaction())) {
176 return false;
177 }
178 }
179 kernel_->put(IS_UNAPPLIED_UPDATE, value);
180 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
181 }
182 return true;
183 }
184
PutServerIsDir(bool value)185 void ModelNeutralMutableEntry::PutServerIsDir(bool value) {
186 DCHECK(kernel_);
187 base_write_transaction_->TrackChangesTo(kernel_);
188 bool old_value = kernel_->ref(SERVER_IS_DIR);
189 if (old_value != value) {
190 kernel_->put(SERVER_IS_DIR, value);
191 kernel_->mark_dirty(GetDirtyIndexHelper());
192 }
193 }
194
PutServerIsDel(bool value)195 void ModelNeutralMutableEntry::PutServerIsDel(bool value) {
196 DCHECK(kernel_);
197 base_write_transaction_->TrackChangesTo(kernel_);
198 bool old_value = kernel_->ref(SERVER_IS_DEL);
199 if (old_value != value) {
200 kernel_->put(SERVER_IS_DEL, value);
201 kernel_->mark_dirty(GetDirtyIndexHelper());
202 }
203
204 // Update delete journal for existence status change on server side here
205 // instead of in PutIsDel() because IS_DEL may not be updated due to
206 // early returns when processing updates. And because
207 // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has
208 // to be called on sync thread.
209 dir()->delete_journal()->UpdateDeleteJournalForServerDelete(
210 base_write_transaction(), old_value, *kernel_);
211 }
212
PutServerNonUniqueName(const std::string & value)213 void ModelNeutralMutableEntry::PutServerNonUniqueName(
214 const std::string& value) {
215 DCHECK(kernel_);
216 base_write_transaction_->TrackChangesTo(kernel_);
217
218 if (kernel_->ref(SERVER_NON_UNIQUE_NAME) != value) {
219 kernel_->put(SERVER_NON_UNIQUE_NAME, value);
220 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
221 }
222 }
223
PutUniqueServerTag(const string & new_tag)224 bool ModelNeutralMutableEntry::PutUniqueServerTag(const string& new_tag) {
225 if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) {
226 return true;
227 }
228
229 base_write_transaction_->TrackChangesTo(kernel_);
230 ScopedKernelLock lock(dir());
231 // Make sure your new value is not in there already.
232 if (dir()->kernel_->server_tags_map.find(new_tag) !=
233 dir()->kernel_->server_tags_map.end()) {
234 DVLOG(1) << "Detected duplicate server tag";
235 return false;
236 }
237 dir()->kernel_->server_tags_map.erase(
238 kernel_->ref(UNIQUE_SERVER_TAG));
239 kernel_->put(UNIQUE_SERVER_TAG, new_tag);
240 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
241 if (!new_tag.empty()) {
242 dir()->kernel_->server_tags_map[new_tag] = kernel_;
243 }
244
245 return true;
246 }
247
PutUniqueClientTag(const string & new_tag)248 bool ModelNeutralMutableEntry::PutUniqueClientTag(const string& new_tag) {
249 if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) {
250 return true;
251 }
252
253 base_write_transaction_->TrackChangesTo(kernel_);
254 ScopedKernelLock lock(dir());
255 // Make sure your new value is not in there already.
256 if (dir()->kernel_->client_tags_map.find(new_tag) !=
257 dir()->kernel_->client_tags_map.end()) {
258 DVLOG(1) << "Detected duplicate client tag";
259 return false;
260 }
261 dir()->kernel_->client_tags_map.erase(
262 kernel_->ref(UNIQUE_CLIENT_TAG));
263 kernel_->put(UNIQUE_CLIENT_TAG, new_tag);
264 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
265 if (!new_tag.empty()) {
266 dir()->kernel_->client_tags_map[new_tag] = kernel_;
267 }
268
269 return true;
270 }
271
PutUniqueBookmarkTag(const std::string & tag)272 void ModelNeutralMutableEntry::PutUniqueBookmarkTag(const std::string& tag) {
273 // This unique tag will eventually be used as the unique suffix when adjusting
274 // this bookmark's position. Let's make sure it's a valid suffix.
275 if (!UniquePosition::IsValidSuffix(tag)) {
276 NOTREACHED();
277 return;
278 }
279
280 if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty() &&
281 tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) {
282 // There is only one scenario where our tag is expected to change. That
283 // scenario occurs when our current tag is a non-correct tag assigned during
284 // the UniquePosition migration.
285 std::string migration_generated_tag =
286 GenerateSyncableBookmarkHash(std::string(),
287 kernel_->ref(ID).GetServerId());
288 DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG));
289 }
290
291 kernel_->put(UNIQUE_BOOKMARK_TAG, tag);
292 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
293 }
294
PutServerSpecifics(const sync_pb::EntitySpecifics & value)295 void ModelNeutralMutableEntry::PutServerSpecifics(
296 const sync_pb::EntitySpecifics& value) {
297 DCHECK(kernel_);
298 CHECK(!value.password().has_client_only_encrypted_data());
299 base_write_transaction_->TrackChangesTo(kernel_);
300 // TODO(ncarter): This is unfortunately heavyweight. Can we do
301 // better?
302 if (kernel_->ref(SERVER_SPECIFICS).SerializeAsString() !=
303 value.SerializeAsString()) {
304 if (kernel_->ref(IS_UNAPPLIED_UPDATE)) {
305 // Remove ourselves from unapplied_update_metahandles with our
306 // old server type.
307 const ModelType old_server_type = kernel_->GetServerModelType();
308 const int64 metahandle = kernel_->ref(META_HANDLE);
309 size_t erase_count =
310 dir()->kernel_->unapplied_update_metahandles[old_server_type]
311 .erase(metahandle);
312 DCHECK_EQ(erase_count, 1u);
313 }
314
315 kernel_->put(SERVER_SPECIFICS, value);
316 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
317
318 if (kernel_->ref(IS_UNAPPLIED_UPDATE)) {
319 // Add ourselves back into unapplied_update_metahandles with our
320 // new server type.
321 const ModelType new_server_type = kernel_->GetServerModelType();
322 const int64 metahandle = kernel_->ref(META_HANDLE);
323 dir()->kernel_->unapplied_update_metahandles[new_server_type]
324 .insert(metahandle);
325 }
326 }
327 }
328
PutBaseServerSpecifics(const sync_pb::EntitySpecifics & value)329 void ModelNeutralMutableEntry::PutBaseServerSpecifics(
330 const sync_pb::EntitySpecifics& value) {
331 DCHECK(kernel_);
332 CHECK(!value.password().has_client_only_encrypted_data());
333 base_write_transaction_->TrackChangesTo(kernel_);
334 // TODO(ncarter): This is unfortunately heavyweight. Can we do
335 // better?
336 if (kernel_->ref(BASE_SERVER_SPECIFICS).SerializeAsString()
337 != value.SerializeAsString()) {
338 kernel_->put(BASE_SERVER_SPECIFICS, value);
339 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
340 }
341 }
342
PutServerUniquePosition(const UniquePosition & value)343 void ModelNeutralMutableEntry::PutServerUniquePosition(
344 const UniquePosition& value) {
345 DCHECK(kernel_);
346 base_write_transaction_->TrackChangesTo(kernel_);
347 if(!kernel_->ref(SERVER_UNIQUE_POSITION).Equals(value)) {
348 // We should never overwrite a valid position with an invalid one.
349 DCHECK(value.IsValid());
350 ScopedKernelLock lock(dir());
351 kernel_->put(SERVER_UNIQUE_POSITION, value);
352 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
353 }
354 }
355
PutSyncing(bool value)356 void ModelNeutralMutableEntry::PutSyncing(bool value) {
357 kernel_->put(SYNCING, value);
358 }
359
PutParentIdPropertyOnly(const Id & parent_id)360 void ModelNeutralMutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
361 base_write_transaction_->TrackChangesTo(kernel_);
362 dir()->ReindexParentId(base_write_transaction(), kernel_, parent_id);
363 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles);
364 }
365
UpdateTransactionVersion(int64 value)366 void ModelNeutralMutableEntry::UpdateTransactionVersion(int64 value) {
367 ScopedKernelLock lock(dir());
368 kernel_->put(TRANSACTION_VERSION, value);
369 kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles));
370 }
371
ModelNeutralMutableEntry(BaseWriteTransaction * trans)372 ModelNeutralMutableEntry::ModelNeutralMutableEntry(BaseWriteTransaction* trans)
373 : Entry(trans), base_write_transaction_(trans) {}
374
GetDirtyIndexHelper()375 MetahandleSet* ModelNeutralMutableEntry::GetDirtyIndexHelper() {
376 return &dir()->kernel_->dirty_metahandles;
377 }
378
379 } // namespace syncable
380
381 } // namespace syncer
382