• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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