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/directory.h"
6
7 #include <iterator>
8
9 #include "base/base64.h"
10 #include "base/debug/trace_event.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "sync/internal_api/public/base/attachment_id_proto.h"
14 #include "sync/internal_api/public/base/unique_position.h"
15 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
16 #include "sync/syncable/entry.h"
17 #include "sync/syncable/entry_kernel.h"
18 #include "sync/syncable/in_memory_directory_backing_store.h"
19 #include "sync/syncable/on_disk_directory_backing_store.h"
20 #include "sync/syncable/scoped_kernel_lock.h"
21 #include "sync/syncable/scoped_parent_child_index_updater.h"
22 #include "sync/syncable/syncable-inl.h"
23 #include "sync/syncable/syncable_base_transaction.h"
24 #include "sync/syncable/syncable_changes_version.h"
25 #include "sync/syncable/syncable_read_transaction.h"
26 #include "sync/syncable/syncable_util.h"
27 #include "sync/syncable/syncable_write_transaction.h"
28
29 using std::string;
30
31 namespace syncer {
32 namespace syncable {
33
34 // static
35 const base::FilePath::CharType Directory::kSyncDatabaseFilename[] =
36 FILE_PATH_LITERAL("SyncData.sqlite3");
37
PersistedKernelInfo()38 Directory::PersistedKernelInfo::PersistedKernelInfo()
39 : next_id(0) {
40 ModelTypeSet protocol_types = ProtocolTypes();
41 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
42 iter.Inc()) {
43 ResetDownloadProgress(iter.Get());
44 transaction_version[iter.Get()] = 0;
45 }
46 }
47
~PersistedKernelInfo()48 Directory::PersistedKernelInfo::~PersistedKernelInfo() {}
49
ResetDownloadProgress(ModelType model_type)50 void Directory::PersistedKernelInfo::ResetDownloadProgress(
51 ModelType model_type) {
52 // Clear everything except the data type id field.
53 download_progress[model_type].Clear();
54 download_progress[model_type].set_data_type_id(
55 GetSpecificsFieldNumberFromModelType(model_type));
56
57 // Explicitly set an empty token field to denote no progress.
58 download_progress[model_type].set_token("");
59 }
60
SaveChangesSnapshot()61 Directory::SaveChangesSnapshot::SaveChangesSnapshot()
62 : kernel_info_status(KERNEL_SHARE_INFO_INVALID) {
63 }
64
~SaveChangesSnapshot()65 Directory::SaveChangesSnapshot::~SaveChangesSnapshot() {
66 STLDeleteElements(&dirty_metas);
67 STLDeleteElements(&delete_journals);
68 }
69
Kernel(const std::string & name,const KernelLoadInfo & info,DirectoryChangeDelegate * delegate,const WeakHandle<TransactionObserver> & transaction_observer)70 Directory::Kernel::Kernel(
71 const std::string& name,
72 const KernelLoadInfo& info, DirectoryChangeDelegate* delegate,
73 const WeakHandle<TransactionObserver>& transaction_observer)
74 : next_write_transaction_id(0),
75 name(name),
76 info_status(Directory::KERNEL_SHARE_INFO_VALID),
77 persisted_info(info.kernel_info),
78 cache_guid(info.cache_guid),
79 next_metahandle(info.max_metahandle + 1),
80 delegate(delegate),
81 transaction_observer(transaction_observer) {
82 DCHECK(delegate);
83 DCHECK(transaction_observer.IsInitialized());
84 }
85
~Kernel()86 Directory::Kernel::~Kernel() {
87 STLDeleteContainerPairSecondPointers(metahandles_map.begin(),
88 metahandles_map.end());
89 }
90
Directory(DirectoryBackingStore * store,UnrecoverableErrorHandler * unrecoverable_error_handler,ReportUnrecoverableErrorFunction report_unrecoverable_error_function,NigoriHandler * nigori_handler,Cryptographer * cryptographer)91 Directory::Directory(
92 DirectoryBackingStore* store,
93 UnrecoverableErrorHandler* unrecoverable_error_handler,
94 ReportUnrecoverableErrorFunction report_unrecoverable_error_function,
95 NigoriHandler* nigori_handler,
96 Cryptographer* cryptographer)
97 : kernel_(NULL),
98 store_(store),
99 unrecoverable_error_handler_(unrecoverable_error_handler),
100 report_unrecoverable_error_function_(
101 report_unrecoverable_error_function),
102 unrecoverable_error_set_(false),
103 nigori_handler_(nigori_handler),
104 cryptographer_(cryptographer),
105 invariant_check_level_(VERIFY_CHANGES) {
106 }
107
~Directory()108 Directory::~Directory() {
109 Close();
110 }
111
Open(const string & name,DirectoryChangeDelegate * delegate,const WeakHandle<TransactionObserver> & transaction_observer)112 DirOpenResult Directory::Open(
113 const string& name,
114 DirectoryChangeDelegate* delegate,
115 const WeakHandle<TransactionObserver>& transaction_observer) {
116 TRACE_EVENT0("sync", "SyncDatabaseOpen");
117
118 const DirOpenResult result =
119 OpenImpl(name, delegate, transaction_observer);
120
121 if (OPENED != result)
122 Close();
123 return result;
124 }
125
InitializeIndices(MetahandlesMap * handles_map)126 void Directory::InitializeIndices(MetahandlesMap* handles_map) {
127 ScopedKernelLock lock(this);
128 kernel_->metahandles_map.swap(*handles_map);
129 for (MetahandlesMap::const_iterator it = kernel_->metahandles_map.begin();
130 it != kernel_->metahandles_map.end(); ++it) {
131 EntryKernel* entry = it->second;
132 if (ParentChildIndex::ShouldInclude(entry))
133 kernel_->parent_child_index.Insert(entry);
134 const int64 metahandle = entry->ref(META_HANDLE);
135 if (entry->ref(IS_UNSYNCED))
136 kernel_->unsynced_metahandles.insert(metahandle);
137 if (entry->ref(IS_UNAPPLIED_UPDATE)) {
138 const ModelType type = entry->GetServerModelType();
139 kernel_->unapplied_update_metahandles[type].insert(metahandle);
140 }
141 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
142 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) ==
143 kernel_->server_tags_map.end())
144 << "Unexpected duplicate use of client tag";
145 kernel_->server_tags_map[entry->ref(UNIQUE_SERVER_TAG)] = entry;
146 }
147 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
148 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) ==
149 kernel_->server_tags_map.end())
150 << "Unexpected duplicate use of server tag";
151 kernel_->client_tags_map[entry->ref(UNIQUE_CLIENT_TAG)] = entry;
152 }
153 DCHECK(kernel_->ids_map.find(entry->ref(ID).value()) ==
154 kernel_->ids_map.end()) << "Unexpected duplicate use of ID";
155 kernel_->ids_map[entry->ref(ID).value()] = entry;
156 DCHECK(!entry->is_dirty());
157 AddToAttachmentIndex(metahandle, entry->ref(ATTACHMENT_METADATA), lock);
158 }
159 }
160
OpenImpl(const string & name,DirectoryChangeDelegate * delegate,const WeakHandle<TransactionObserver> & transaction_observer)161 DirOpenResult Directory::OpenImpl(
162 const string& name,
163 DirectoryChangeDelegate* delegate,
164 const WeakHandle<TransactionObserver>&
165 transaction_observer) {
166 KernelLoadInfo info;
167 // Temporary indices before kernel_ initialized in case Load fails. We 0(1)
168 // swap these later.
169 Directory::MetahandlesMap tmp_handles_map;
170
171 // Avoids mem leaks on failure. Harmlessly deletes the empty hash map after
172 // the swap in the success case.
173 STLValueDeleter<Directory::MetahandlesMap> deleter(&tmp_handles_map);
174
175 JournalIndex delete_journals;
176
177 DirOpenResult result =
178 store_->Load(&tmp_handles_map, &delete_journals, &info);
179 if (OPENED != result)
180 return result;
181
182 kernel_ = new Kernel(name, info, delegate, transaction_observer);
183 delete_journal_.reset(new DeleteJournal(&delete_journals));
184 InitializeIndices(&tmp_handles_map);
185
186 // Write back the share info to reserve some space in 'next_id'. This will
187 // prevent local ID reuse in the case of an early crash. See the comments in
188 // TakeSnapshotForSaveChanges() or crbug.com/142987 for more information.
189 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
190 if (!SaveChanges())
191 return FAILED_INITIAL_WRITE;
192
193 return OPENED;
194 }
195
delete_journal()196 DeleteJournal* Directory::delete_journal() {
197 DCHECK(delete_journal_.get());
198 return delete_journal_.get();
199 }
200
Close()201 void Directory::Close() {
202 store_.reset();
203 if (kernel_) {
204 delete kernel_;
205 kernel_ = NULL;
206 }
207 }
208
OnUnrecoverableError(const BaseTransaction * trans,const tracked_objects::Location & location,const std::string & message)209 void Directory::OnUnrecoverableError(const BaseTransaction* trans,
210 const tracked_objects::Location& location,
211 const std::string & message) {
212 DCHECK(trans != NULL);
213 unrecoverable_error_set_ = true;
214 unrecoverable_error_handler_->OnUnrecoverableError(location,
215 message);
216 }
217
GetEntryById(const Id & id)218 EntryKernel* Directory::GetEntryById(const Id& id) {
219 ScopedKernelLock lock(this);
220 return GetEntryById(id, &lock);
221 }
222
GetEntryById(const Id & id,ScopedKernelLock * const lock)223 EntryKernel* Directory::GetEntryById(const Id& id,
224 ScopedKernelLock* const lock) {
225 DCHECK(kernel_);
226 // Find it in the in memory ID index.
227 IdsMap::iterator id_found = kernel_->ids_map.find(id.value());
228 if (id_found != kernel_->ids_map.end()) {
229 return id_found->second;
230 }
231 return NULL;
232 }
233
GetEntryByClientTag(const string & tag)234 EntryKernel* Directory::GetEntryByClientTag(const string& tag) {
235 ScopedKernelLock lock(this);
236 DCHECK(kernel_);
237
238 TagsMap::iterator it = kernel_->client_tags_map.find(tag);
239 if (it != kernel_->client_tags_map.end()) {
240 return it->second;
241 }
242 return NULL;
243 }
244
GetEntryByServerTag(const string & tag)245 EntryKernel* Directory::GetEntryByServerTag(const string& tag) {
246 ScopedKernelLock lock(this);
247 DCHECK(kernel_);
248 TagsMap::iterator it = kernel_->server_tags_map.find(tag);
249 if (it != kernel_->server_tags_map.end()) {
250 return it->second;
251 }
252 return NULL;
253 }
254
GetEntryByHandle(int64 metahandle)255 EntryKernel* Directory::GetEntryByHandle(int64 metahandle) {
256 ScopedKernelLock lock(this);
257 return GetEntryByHandle(metahandle, &lock);
258 }
259
GetEntryByHandle(int64 metahandle,ScopedKernelLock * lock)260 EntryKernel* Directory::GetEntryByHandle(int64 metahandle,
261 ScopedKernelLock* lock) {
262 // Look up in memory
263 MetahandlesMap::iterator found =
264 kernel_->metahandles_map.find(metahandle);
265 if (found != kernel_->metahandles_map.end()) {
266 // Found it in memory. Easy.
267 return found->second;
268 }
269 return NULL;
270 }
271
GetChildHandlesById(BaseTransaction * trans,const Id & parent_id,Directory::Metahandles * result)272 bool Directory::GetChildHandlesById(
273 BaseTransaction* trans, const Id& parent_id,
274 Directory::Metahandles* result) {
275 if (!SyncAssert(this == trans->directory(), FROM_HERE,
276 "Directories don't match", trans))
277 return false;
278 result->clear();
279
280 ScopedKernelLock lock(this);
281 AppendChildHandles(lock, parent_id, result);
282 return true;
283 }
284
GetTotalNodeCount(BaseTransaction * trans,EntryKernel * kernel) const285 int Directory::GetTotalNodeCount(
286 BaseTransaction* trans,
287 EntryKernel* kernel) const {
288 if (!SyncAssert(this == trans->directory(), FROM_HERE,
289 "Directories don't match", trans))
290 return false;
291
292 int count = 1;
293 std::deque<const OrderedChildSet*> child_sets;
294
295 GetChildSetForKernel(trans, kernel, &child_sets);
296 while (!child_sets.empty()) {
297 const OrderedChildSet* set = child_sets.front();
298 child_sets.pop_front();
299 for (OrderedChildSet::const_iterator it = set->begin();
300 it != set->end(); ++it) {
301 count++;
302 GetChildSetForKernel(trans, *it, &child_sets);
303 }
304 }
305
306 return count;
307 }
308
GetChildSetForKernel(BaseTransaction * trans,EntryKernel * kernel,std::deque<const OrderedChildSet * > * child_sets) const309 void Directory::GetChildSetForKernel(
310 BaseTransaction* trans,
311 EntryKernel* kernel,
312 std::deque<const OrderedChildSet*>* child_sets) const {
313 if (!kernel->ref(IS_DIR))
314 return; // Not a directory => no children.
315
316 const OrderedChildSet* descendants =
317 kernel_->parent_child_index.GetChildren(kernel->ref(ID));
318 if (!descendants)
319 return; // This directory has no children.
320
321 // Add our children to the list of items to be traversed.
322 child_sets->push_back(descendants);
323 }
324
GetPositionIndex(BaseTransaction * trans,EntryKernel * kernel) const325 int Directory::GetPositionIndex(
326 BaseTransaction* trans,
327 EntryKernel* kernel) const {
328 const OrderedChildSet* siblings =
329 kernel_->parent_child_index.GetChildren(kernel->ref(PARENT_ID));
330
331 OrderedChildSet::const_iterator it = siblings->find(kernel);
332 return std::distance(siblings->begin(), it);
333 }
334
InsertEntry(BaseWriteTransaction * trans,EntryKernel * entry)335 bool Directory::InsertEntry(BaseWriteTransaction* trans, EntryKernel* entry) {
336 ScopedKernelLock lock(this);
337 return InsertEntry(trans, entry, &lock);
338 }
339
InsertEntry(BaseWriteTransaction * trans,EntryKernel * entry,ScopedKernelLock * lock)340 bool Directory::InsertEntry(BaseWriteTransaction* trans,
341 EntryKernel* entry,
342 ScopedKernelLock* lock) {
343 DCHECK(NULL != lock);
344 if (!SyncAssert(NULL != entry, FROM_HERE, "Entry is null", trans))
345 return false;
346
347 static const char error[] = "Entry already in memory index.";
348
349 if (!SyncAssert(
350 kernel_->metahandles_map.insert(
351 std::make_pair(entry->ref(META_HANDLE), entry)).second,
352 FROM_HERE,
353 error,
354 trans)) {
355 return false;
356 }
357 if (!SyncAssert(
358 kernel_->ids_map.insert(
359 std::make_pair(entry->ref(ID).value(), entry)).second,
360 FROM_HERE,
361 error,
362 trans)) {
363 return false;
364 }
365 if (ParentChildIndex::ShouldInclude(entry)) {
366 if (!SyncAssert(kernel_->parent_child_index.Insert(entry),
367 FROM_HERE,
368 error,
369 trans)) {
370 return false;
371 }
372 }
373 AddToAttachmentIndex(
374 entry->ref(META_HANDLE), entry->ref(ATTACHMENT_METADATA), *lock);
375
376 // Should NEVER be created with a client tag or server tag.
377 if (!SyncAssert(entry->ref(UNIQUE_SERVER_TAG).empty(), FROM_HERE,
378 "Server tag should be empty", trans)) {
379 return false;
380 }
381 if (!SyncAssert(entry->ref(UNIQUE_CLIENT_TAG).empty(), FROM_HERE,
382 "Client tag should be empty", trans))
383 return false;
384
385 return true;
386 }
387
ReindexId(BaseWriteTransaction * trans,EntryKernel * const entry,const Id & new_id)388 bool Directory::ReindexId(BaseWriteTransaction* trans,
389 EntryKernel* const entry,
390 const Id& new_id) {
391 ScopedKernelLock lock(this);
392 if (NULL != GetEntryById(new_id, &lock))
393 return false;
394
395 {
396 // Update the indices that depend on the ID field.
397 ScopedParentChildIndexUpdater updater_b(lock, entry,
398 &kernel_->parent_child_index);
399 size_t num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
400 DCHECK_EQ(1U, num_erased);
401 entry->put(ID, new_id);
402 kernel_->ids_map[entry->ref(ID).value()] = entry;
403 }
404 return true;
405 }
406
ReindexParentId(BaseWriteTransaction * trans,EntryKernel * const entry,const Id & new_parent_id)407 bool Directory::ReindexParentId(BaseWriteTransaction* trans,
408 EntryKernel* const entry,
409 const Id& new_parent_id) {
410 ScopedKernelLock lock(this);
411
412 {
413 // Update the indices that depend on the PARENT_ID field.
414 ScopedParentChildIndexUpdater index_updater(lock, entry,
415 &kernel_->parent_child_index);
416 entry->put(PARENT_ID, new_parent_id);
417 }
418 return true;
419 }
420
RemoveFromAttachmentIndex(const int64 metahandle,const sync_pb::AttachmentMetadata & attachment_metadata,const ScopedKernelLock & lock)421 void Directory::RemoveFromAttachmentIndex(
422 const int64 metahandle,
423 const sync_pb::AttachmentMetadata& attachment_metadata,
424 const ScopedKernelLock& lock) {
425 for (int i = 0; i < attachment_metadata.record_size(); ++i) {
426 AttachmentIdUniqueId unique_id =
427 attachment_metadata.record(i).id().unique_id();
428 IndexByAttachmentId::iterator iter =
429 kernel_->index_by_attachment_id.find(unique_id);
430 if (iter != kernel_->index_by_attachment_id.end()) {
431 iter->second.erase(metahandle);
432 if (iter->second.empty()) {
433 kernel_->index_by_attachment_id.erase(iter);
434 }
435 }
436 }
437 }
438
AddToAttachmentIndex(const int64 metahandle,const sync_pb::AttachmentMetadata & attachment_metadata,const ScopedKernelLock & lock)439 void Directory::AddToAttachmentIndex(
440 const int64 metahandle,
441 const sync_pb::AttachmentMetadata& attachment_metadata,
442 const ScopedKernelLock& lock) {
443 for (int i = 0; i < attachment_metadata.record_size(); ++i) {
444 AttachmentIdUniqueId unique_id =
445 attachment_metadata.record(i).id().unique_id();
446 IndexByAttachmentId::iterator iter =
447 kernel_->index_by_attachment_id.find(unique_id);
448 if (iter == kernel_->index_by_attachment_id.end()) {
449 iter = kernel_->index_by_attachment_id.insert(std::make_pair(
450 unique_id,
451 MetahandleSet())).first;
452 }
453 iter->second.insert(metahandle);
454 }
455 }
456
UpdateAttachmentIndex(const int64 metahandle,const sync_pb::AttachmentMetadata & old_metadata,const sync_pb::AttachmentMetadata & new_metadata)457 void Directory::UpdateAttachmentIndex(
458 const int64 metahandle,
459 const sync_pb::AttachmentMetadata& old_metadata,
460 const sync_pb::AttachmentMetadata& new_metadata) {
461 ScopedKernelLock lock(this);
462 RemoveFromAttachmentIndex(metahandle, old_metadata, lock);
463 AddToAttachmentIndex(metahandle, new_metadata, lock);
464 }
465
GetMetahandlesByAttachmentId(BaseTransaction * trans,const sync_pb::AttachmentIdProto & attachment_id_proto,Metahandles * result)466 void Directory::GetMetahandlesByAttachmentId(
467 BaseTransaction* trans,
468 const sync_pb::AttachmentIdProto& attachment_id_proto,
469 Metahandles* result) {
470 DCHECK(result);
471 result->clear();
472 ScopedKernelLock lock(this);
473 IndexByAttachmentId::const_iterator index_iter =
474 kernel_->index_by_attachment_id.find(attachment_id_proto.unique_id());
475 if (index_iter == kernel_->index_by_attachment_id.end())
476 return;
477 const MetahandleSet& metahandle_set = index_iter->second;
478 std::copy(
479 metahandle_set.begin(), metahandle_set.end(), back_inserter(*result));
480 }
481
unrecoverable_error_set(const BaseTransaction * trans) const482 bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const {
483 DCHECK(trans != NULL);
484 return unrecoverable_error_set_;
485 }
486
ClearDirtyMetahandles()487 void Directory::ClearDirtyMetahandles() {
488 kernel_->transaction_mutex.AssertAcquired();
489 kernel_->dirty_metahandles.clear();
490 }
491
SafeToPurgeFromMemory(WriteTransaction * trans,const EntryKernel * const entry) const492 bool Directory::SafeToPurgeFromMemory(WriteTransaction* trans,
493 const EntryKernel* const entry) const {
494 bool safe = entry->ref(IS_DEL) && !entry->is_dirty() &&
495 !entry->ref(SYNCING) && !entry->ref(IS_UNAPPLIED_UPDATE) &&
496 !entry->ref(IS_UNSYNCED);
497
498 if (safe) {
499 int64 handle = entry->ref(META_HANDLE);
500 const ModelType type = entry->GetServerModelType();
501 if (!SyncAssert(kernel_->dirty_metahandles.count(handle) == 0U,
502 FROM_HERE,
503 "Dirty metahandles should be empty", trans))
504 return false;
505 // TODO(tim): Bug 49278.
506 if (!SyncAssert(!kernel_->unsynced_metahandles.count(handle),
507 FROM_HERE,
508 "Unsynced handles should be empty",
509 trans))
510 return false;
511 if (!SyncAssert(!kernel_->unapplied_update_metahandles[type].count(handle),
512 FROM_HERE,
513 "Unapplied metahandles should be empty",
514 trans))
515 return false;
516 }
517
518 return safe;
519 }
520
TakeSnapshotForSaveChanges(SaveChangesSnapshot * snapshot)521 void Directory::TakeSnapshotForSaveChanges(SaveChangesSnapshot* snapshot) {
522 ReadTransaction trans(FROM_HERE, this);
523 ScopedKernelLock lock(this);
524
525 // If there is an unrecoverable error then just bail out.
526 if (unrecoverable_error_set(&trans))
527 return;
528
529 // Deep copy dirty entries from kernel_->metahandles_index into snapshot and
530 // clear dirty flags.
531 for (MetahandleSet::const_iterator i = kernel_->dirty_metahandles.begin();
532 i != kernel_->dirty_metahandles.end(); ++i) {
533 EntryKernel* entry = GetEntryByHandle(*i, &lock);
534 if (!entry)
535 continue;
536 // Skip over false positives; it happens relatively infrequently.
537 if (!entry->is_dirty())
538 continue;
539 snapshot->dirty_metas.insert(snapshot->dirty_metas.end(),
540 new EntryKernel(*entry));
541 DCHECK_EQ(1U, kernel_->dirty_metahandles.count(*i));
542 // We don't bother removing from the index here as we blow the entire thing
543 // in a moment, and it unnecessarily complicates iteration.
544 entry->clear_dirty(NULL);
545 }
546 ClearDirtyMetahandles();
547
548 // Set purged handles.
549 DCHECK(snapshot->metahandles_to_purge.empty());
550 snapshot->metahandles_to_purge.swap(kernel_->metahandles_to_purge);
551
552 // Fill kernel_info_status and kernel_info.
553 snapshot->kernel_info = kernel_->persisted_info;
554 // To avoid duplicates when the process crashes, we record the next_id to be
555 // greater magnitude than could possibly be reached before the next save
556 // changes. In other words, it's effectively impossible for the user to
557 // generate 65536 new bookmarks in 3 seconds.
558 snapshot->kernel_info.next_id -= 65536;
559 snapshot->kernel_info_status = kernel_->info_status;
560 // This one we reset on failure.
561 kernel_->info_status = KERNEL_SHARE_INFO_VALID;
562
563 delete_journal_->TakeSnapshotAndClear(
564 &trans, &snapshot->delete_journals, &snapshot->delete_journals_to_purge);
565 }
566
SaveChanges()567 bool Directory::SaveChanges() {
568 bool success = false;
569
570 base::AutoLock scoped_lock(kernel_->save_changes_mutex);
571
572 // Snapshot and save.
573 SaveChangesSnapshot snapshot;
574 TakeSnapshotForSaveChanges(&snapshot);
575 success = store_->SaveChanges(snapshot);
576
577 // Handle success or failure.
578 if (success)
579 success = VacuumAfterSaveChanges(snapshot);
580 else
581 HandleSaveChangesFailure(snapshot);
582 return success;
583 }
584
VacuumAfterSaveChanges(const SaveChangesSnapshot & snapshot)585 bool Directory::VacuumAfterSaveChanges(const SaveChangesSnapshot& snapshot) {
586 if (snapshot.dirty_metas.empty())
587 return true;
588
589 // Need a write transaction as we are about to permanently purge entries.
590 WriteTransaction trans(FROM_HERE, VACUUM_AFTER_SAVE, this);
591 ScopedKernelLock lock(this);
592 // Now drop everything we can out of memory.
593 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
594 i != snapshot.dirty_metas.end(); ++i) {
595 MetahandlesMap::iterator found =
596 kernel_->metahandles_map.find((*i)->ref(META_HANDLE));
597 EntryKernel* entry = (found == kernel_->metahandles_map.end() ?
598 NULL : found->second);
599 if (entry && SafeToPurgeFromMemory(&trans, entry)) {
600 // We now drop deleted metahandles that are up to date on both the client
601 // and the server.
602 size_t num_erased = 0;
603 num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE));
604 DCHECK_EQ(1u, num_erased);
605 num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
606 DCHECK_EQ(1u, num_erased);
607 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
608 num_erased =
609 kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG));
610 DCHECK_EQ(1u, num_erased);
611 }
612 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
613 num_erased =
614 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG));
615 DCHECK_EQ(1u, num_erased);
616 }
617 if (!SyncAssert(!kernel_->parent_child_index.Contains(entry),
618 FROM_HERE,
619 "Deleted entry still present",
620 (&trans)))
621 return false;
622 RemoveFromAttachmentIndex(
623 entry->ref(META_HANDLE), entry->ref(ATTACHMENT_METADATA), lock);
624
625 delete entry;
626 }
627 if (trans.unrecoverable_error_set())
628 return false;
629 }
630 return true;
631 }
632
UnapplyEntry(EntryKernel * entry)633 void Directory::UnapplyEntry(EntryKernel* entry) {
634 int64 handle = entry->ref(META_HANDLE);
635 ModelType server_type = GetModelTypeFromSpecifics(
636 entry->ref(SERVER_SPECIFICS));
637
638 // Clear enough so that on the next sync cycle all local data will
639 // be overwritten.
640 // Note: do not modify the root node in order to preserve the
641 // initial sync ended bit for this type (else on the next restart
642 // this type will be treated as disabled and therefore fully purged).
643 if (IsRealDataType(server_type) &&
644 ModelTypeToRootTag(server_type) == entry->ref(UNIQUE_SERVER_TAG)) {
645 return;
646 }
647
648 // Set the unapplied bit if this item has server data.
649 if (IsRealDataType(server_type) && !entry->ref(IS_UNAPPLIED_UPDATE)) {
650 entry->put(IS_UNAPPLIED_UPDATE, true);
651 kernel_->unapplied_update_metahandles[server_type].insert(handle);
652 entry->mark_dirty(&kernel_->dirty_metahandles);
653 }
654
655 // Unset the unsynced bit.
656 if (entry->ref(IS_UNSYNCED)) {
657 kernel_->unsynced_metahandles.erase(handle);
658 entry->put(IS_UNSYNCED, false);
659 entry->mark_dirty(&kernel_->dirty_metahandles);
660 }
661
662 // Mark the item as locally deleted. No deleted items are allowed in the
663 // parent child index.
664 if (!entry->ref(IS_DEL)) {
665 kernel_->parent_child_index.Remove(entry);
666 entry->put(IS_DEL, true);
667 entry->mark_dirty(&kernel_->dirty_metahandles);
668 }
669
670 // Set the version to the "newly created" version.
671 if (entry->ref(BASE_VERSION) != CHANGES_VERSION) {
672 entry->put(BASE_VERSION, CHANGES_VERSION);
673 entry->mark_dirty(&kernel_->dirty_metahandles);
674 }
675
676 // At this point locally created items that aren't synced will become locally
677 // deleted items, and purged on the next snapshot. All other items will match
678 // the state they would have had if they were just created via a server
679 // update. See MutableEntry::MutableEntry(.., CreateNewUpdateItem, ..).
680 }
681
DeleteEntry(bool save_to_journal,EntryKernel * entry,EntryKernelSet * entries_to_journal,const ScopedKernelLock & lock)682 void Directory::DeleteEntry(bool save_to_journal,
683 EntryKernel* entry,
684 EntryKernelSet* entries_to_journal,
685 const ScopedKernelLock& lock) {
686 int64 handle = entry->ref(META_HANDLE);
687 ModelType server_type = GetModelTypeFromSpecifics(
688 entry->ref(SERVER_SPECIFICS));
689
690 kernel_->metahandles_to_purge.insert(handle);
691
692 size_t num_erased = 0;
693 num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE));
694 DCHECK_EQ(1u, num_erased);
695 num_erased = kernel_->ids_map.erase(entry->ref(ID).value());
696 DCHECK_EQ(1u, num_erased);
697 num_erased = kernel_->unsynced_metahandles.erase(handle);
698 DCHECK_EQ(entry->ref(IS_UNSYNCED), num_erased > 0);
699 num_erased =
700 kernel_->unapplied_update_metahandles[server_type].erase(handle);
701 DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0);
702 if (kernel_->parent_child_index.Contains(entry))
703 kernel_->parent_child_index.Remove(entry);
704
705 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) {
706 num_erased =
707 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG));
708 DCHECK_EQ(1u, num_erased);
709 }
710 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) {
711 num_erased =
712 kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG));
713 DCHECK_EQ(1u, num_erased);
714 }
715 RemoveFromAttachmentIndex(handle, entry->ref(ATTACHMENT_METADATA), lock);
716
717 if (save_to_journal) {
718 entries_to_journal->insert(entry);
719 } else {
720 delete entry;
721 }
722 }
723
PurgeEntriesWithTypeIn(ModelTypeSet disabled_types,ModelTypeSet types_to_journal,ModelTypeSet types_to_unapply)724 bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet disabled_types,
725 ModelTypeSet types_to_journal,
726 ModelTypeSet types_to_unapply) {
727 disabled_types.RemoveAll(ProxyTypes());
728
729 if (disabled_types.Empty())
730 return true;
731
732 {
733 WriteTransaction trans(FROM_HERE, PURGE_ENTRIES, this);
734
735 EntryKernelSet entries_to_journal;
736 STLElementDeleter<EntryKernelSet> journal_deleter(&entries_to_journal);
737
738 {
739 ScopedKernelLock lock(this);
740
741 // We iterate in two passes to avoid a bug in STLport (which is used in
742 // the Android build). There are some versions of that library where a
743 // hash_map's iterators can be invalidated when an item is erased from the
744 // hash_map.
745 // See http://sourceforge.net/p/stlport/bugs/239/.
746
747 std::set<EntryKernel*> to_purge;
748 for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
749 it != kernel_->metahandles_map.end(); ++it) {
750 const sync_pb::EntitySpecifics& local_specifics =
751 it->second->ref(SPECIFICS);
752 const sync_pb::EntitySpecifics& server_specifics =
753 it->second->ref(SERVER_SPECIFICS);
754 ModelType local_type = GetModelTypeFromSpecifics(local_specifics);
755 ModelType server_type = GetModelTypeFromSpecifics(server_specifics);
756
757 if ((IsRealDataType(local_type) && disabled_types.Has(local_type)) ||
758 (IsRealDataType(server_type) && disabled_types.Has(server_type))) {
759 to_purge.insert(it->second);
760 }
761 }
762
763 for (std::set<EntryKernel*>::iterator it = to_purge.begin();
764 it != to_purge.end(); ++it) {
765 EntryKernel* entry = *it;
766
767 const sync_pb::EntitySpecifics& local_specifics =
768 (*it)->ref(SPECIFICS);
769 const sync_pb::EntitySpecifics& server_specifics =
770 (*it)->ref(SERVER_SPECIFICS);
771 ModelType local_type = GetModelTypeFromSpecifics(local_specifics);
772 ModelType server_type = GetModelTypeFromSpecifics(server_specifics);
773
774 if (types_to_unapply.Has(local_type) ||
775 types_to_unapply.Has(server_type)) {
776 UnapplyEntry(entry);
777 } else {
778 bool save_to_journal =
779 (types_to_journal.Has(local_type) ||
780 types_to_journal.Has(server_type)) &&
781 (delete_journal_->IsDeleteJournalEnabled(local_type) ||
782 delete_journal_->IsDeleteJournalEnabled(server_type));
783 DeleteEntry(save_to_journal, entry, &entries_to_journal, lock);
784 }
785 }
786
787 delete_journal_->AddJournalBatch(&trans, entries_to_journal);
788
789 // Ensure meta tracking for these data types reflects the purged state.
790 for (ModelTypeSet::Iterator it = disabled_types.First();
791 it.Good(); it.Inc()) {
792 kernel_->persisted_info.transaction_version[it.Get()] = 0;
793
794 // Don't discard progress markers or context for unapplied types.
795 if (!types_to_unapply.Has(it.Get())) {
796 kernel_->persisted_info.ResetDownloadProgress(it.Get());
797 kernel_->persisted_info.datatype_context[it.Get()].Clear();
798 }
799 }
800
801 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
802 }
803 }
804 return true;
805 }
806
ResetVersionsForType(BaseWriteTransaction * trans,ModelType type)807 bool Directory::ResetVersionsForType(BaseWriteTransaction* trans,
808 ModelType type) {
809 if (!ProtocolTypes().Has(type))
810 return false;
811 DCHECK_NE(type, BOOKMARKS) << "Only non-hierarchical types are supported";
812
813 EntryKernel* type_root = GetEntryByServerTag(ModelTypeToRootTag(type));
814 if (!type_root)
815 return false;
816
817 ScopedKernelLock lock(this);
818 const Id& type_root_id = type_root->ref(ID);
819 Directory::Metahandles children;
820 AppendChildHandles(lock, type_root_id, &children);
821
822 for (Metahandles::iterator it = children.begin(); it != children.end();
823 ++it) {
824 EntryKernel* entry = GetEntryByHandle(*it, &lock);
825 if (!entry)
826 continue;
827 if (entry->ref(BASE_VERSION) > 1)
828 entry->put(BASE_VERSION, 1);
829 if (entry->ref(SERVER_VERSION) > 1)
830 entry->put(SERVER_VERSION, 1);
831
832 // Note that we do not unset IS_UNSYNCED or IS_UNAPPLIED_UPDATE in order
833 // to ensure no in-transit data is lost.
834
835 entry->mark_dirty(&kernel_->dirty_metahandles);
836 }
837
838 return true;
839 }
840
IsAttachmentLinked(const sync_pb::AttachmentIdProto & attachment_id_proto) const841 bool Directory::IsAttachmentLinked(
842 const sync_pb::AttachmentIdProto& attachment_id_proto) const {
843 ScopedKernelLock lock(this);
844 IndexByAttachmentId::const_iterator iter =
845 kernel_->index_by_attachment_id.find(attachment_id_proto.unique_id());
846 if (iter != kernel_->index_by_attachment_id.end() && !iter->second.empty()) {
847 return true;
848 }
849 return false;
850 }
851
HandleSaveChangesFailure(const SaveChangesSnapshot & snapshot)852 void Directory::HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot) {
853 WriteTransaction trans(FROM_HERE, HANDLE_SAVE_FAILURE, this);
854 ScopedKernelLock lock(this);
855 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
856
857 // Because we optimistically cleared the dirty bit on the real entries when
858 // taking the snapshot, we must restore it on failure. Not doing this could
859 // cause lost data, if no other changes are made to the in-memory entries
860 // that would cause the dirty bit to get set again. Setting the bit ensures
861 // that SaveChanges will at least try again later.
862 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
863 i != snapshot.dirty_metas.end(); ++i) {
864 MetahandlesMap::iterator found =
865 kernel_->metahandles_map.find((*i)->ref(META_HANDLE));
866 if (found != kernel_->metahandles_map.end()) {
867 found->second->mark_dirty(&kernel_->dirty_metahandles);
868 }
869 }
870
871 kernel_->metahandles_to_purge.insert(snapshot.metahandles_to_purge.begin(),
872 snapshot.metahandles_to_purge.end());
873
874 // Restore delete journals.
875 delete_journal_->AddJournalBatch(&trans, snapshot.delete_journals);
876 delete_journal_->PurgeDeleteJournals(&trans,
877 snapshot.delete_journals_to_purge);
878 }
879
GetDownloadProgress(ModelType model_type,sync_pb::DataTypeProgressMarker * value_out) const880 void Directory::GetDownloadProgress(
881 ModelType model_type,
882 sync_pb::DataTypeProgressMarker* value_out) const {
883 ScopedKernelLock lock(this);
884 return value_out->CopyFrom(
885 kernel_->persisted_info.download_progress[model_type]);
886 }
887
GetDownloadProgressAsString(ModelType model_type,std::string * value_out) const888 void Directory::GetDownloadProgressAsString(
889 ModelType model_type,
890 std::string* value_out) const {
891 ScopedKernelLock lock(this);
892 kernel_->persisted_info.download_progress[model_type].SerializeToString(
893 value_out);
894 }
895
GetEntriesCount() const896 size_t Directory::GetEntriesCount() const {
897 ScopedKernelLock lock(this);
898 return kernel_->metahandles_map.size();
899 }
900
SetDownloadProgress(ModelType model_type,const sync_pb::DataTypeProgressMarker & new_progress)901 void Directory::SetDownloadProgress(
902 ModelType model_type,
903 const sync_pb::DataTypeProgressMarker& new_progress) {
904 ScopedKernelLock lock(this);
905 kernel_->persisted_info.download_progress[model_type].CopyFrom(new_progress);
906 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
907 }
908
GetTransactionVersion(ModelType type) const909 int64 Directory::GetTransactionVersion(ModelType type) const {
910 kernel_->transaction_mutex.AssertAcquired();
911 return kernel_->persisted_info.transaction_version[type];
912 }
913
IncrementTransactionVersion(ModelType type)914 void Directory::IncrementTransactionVersion(ModelType type) {
915 kernel_->transaction_mutex.AssertAcquired();
916 kernel_->persisted_info.transaction_version[type]++;
917 }
918
GetDataTypeContext(BaseTransaction * trans,ModelType type,sync_pb::DataTypeContext * context) const919 void Directory::GetDataTypeContext(BaseTransaction* trans,
920 ModelType type,
921 sync_pb::DataTypeContext* context) const {
922 ScopedKernelLock lock(this);
923 context->CopyFrom(kernel_->persisted_info.datatype_context[type]);
924 }
925
SetDataTypeContext(BaseWriteTransaction * trans,ModelType type,const sync_pb::DataTypeContext & context)926 void Directory::SetDataTypeContext(
927 BaseWriteTransaction* trans,
928 ModelType type,
929 const sync_pb::DataTypeContext& context) {
930 ScopedKernelLock lock(this);
931 kernel_->persisted_info.datatype_context[type].CopyFrom(context);
932 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
933 }
934
InitialSyncEndedTypes()935 ModelTypeSet Directory::InitialSyncEndedTypes() {
936 syncable::ReadTransaction trans(FROM_HERE, this);
937 ModelTypeSet protocol_types = ProtocolTypes();
938 ModelTypeSet initial_sync_ended_types;
939 for (ModelTypeSet::Iterator i = protocol_types.First(); i.Good(); i.Inc()) {
940 if (InitialSyncEndedForType(&trans, i.Get())) {
941 initial_sync_ended_types.Put(i.Get());
942 }
943 }
944 return initial_sync_ended_types;
945 }
946
InitialSyncEndedForType(ModelType type)947 bool Directory::InitialSyncEndedForType(ModelType type) {
948 syncable::ReadTransaction trans(FROM_HERE, this);
949 return InitialSyncEndedForType(&trans, type);
950 }
951
InitialSyncEndedForType(BaseTransaction * trans,ModelType type)952 bool Directory::InitialSyncEndedForType(
953 BaseTransaction* trans, ModelType type) {
954 // True iff the type's root node has been received and applied.
955 syncable::Entry entry(trans, syncable::GET_TYPE_ROOT, type);
956 return entry.good() && entry.GetBaseVersion() != CHANGES_VERSION;
957 }
958
store_birthday() const959 string Directory::store_birthday() const {
960 ScopedKernelLock lock(this);
961 return kernel_->persisted_info.store_birthday;
962 }
963
set_store_birthday(const string & store_birthday)964 void Directory::set_store_birthday(const string& store_birthday) {
965 ScopedKernelLock lock(this);
966 if (kernel_->persisted_info.store_birthday == store_birthday)
967 return;
968 kernel_->persisted_info.store_birthday = store_birthday;
969 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
970 }
971
bag_of_chips() const972 string Directory::bag_of_chips() const {
973 ScopedKernelLock lock(this);
974 return kernel_->persisted_info.bag_of_chips;
975 }
976
set_bag_of_chips(const string & bag_of_chips)977 void Directory::set_bag_of_chips(const string& bag_of_chips) {
978 ScopedKernelLock lock(this);
979 if (kernel_->persisted_info.bag_of_chips == bag_of_chips)
980 return;
981 kernel_->persisted_info.bag_of_chips = bag_of_chips;
982 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
983 }
984
985
cache_guid() const986 string Directory::cache_guid() const {
987 // No need to lock since nothing ever writes to it after load.
988 return kernel_->cache_guid;
989 }
990
GetNigoriHandler()991 NigoriHandler* Directory::GetNigoriHandler() {
992 return nigori_handler_;
993 }
994
GetCryptographer(const BaseTransaction * trans)995 Cryptographer* Directory::GetCryptographer(const BaseTransaction* trans) {
996 DCHECK_EQ(this, trans->directory());
997 return cryptographer_;
998 }
999
GetAllMetaHandles(BaseTransaction * trans,MetahandleSet * result)1000 void Directory::GetAllMetaHandles(BaseTransaction* trans,
1001 MetahandleSet* result) {
1002 result->clear();
1003 ScopedKernelLock lock(this);
1004 for (MetahandlesMap::iterator i = kernel_->metahandles_map.begin();
1005 i != kernel_->metahandles_map.end(); ++i) {
1006 result->insert(i->first);
1007 }
1008 }
1009
GetUnsyncedMetaHandles(BaseTransaction * trans,Metahandles * result)1010 void Directory::GetUnsyncedMetaHandles(BaseTransaction* trans,
1011 Metahandles* result) {
1012 result->clear();
1013 ScopedKernelLock lock(this);
1014 copy(kernel_->unsynced_metahandles.begin(),
1015 kernel_->unsynced_metahandles.end(), back_inserter(*result));
1016 }
1017
unsynced_entity_count() const1018 int64 Directory::unsynced_entity_count() const {
1019 ScopedKernelLock lock(this);
1020 return kernel_->unsynced_metahandles.size();
1021 }
1022
TypeHasUnappliedUpdates(ModelType type)1023 bool Directory::TypeHasUnappliedUpdates(ModelType type) {
1024 ScopedKernelLock lock(this);
1025 return !kernel_->unapplied_update_metahandles[type].empty();
1026 }
1027
GetUnappliedUpdateMetaHandles(BaseTransaction * trans,FullModelTypeSet server_types,std::vector<int64> * result)1028 void Directory::GetUnappliedUpdateMetaHandles(
1029 BaseTransaction* trans,
1030 FullModelTypeSet server_types,
1031 std::vector<int64>* result) {
1032 result->clear();
1033 ScopedKernelLock lock(this);
1034 for (int i = UNSPECIFIED; i < MODEL_TYPE_COUNT; ++i) {
1035 const ModelType type = ModelTypeFromInt(i);
1036 if (server_types.Has(type)) {
1037 std::copy(kernel_->unapplied_update_metahandles[type].begin(),
1038 kernel_->unapplied_update_metahandles[type].end(),
1039 back_inserter(*result));
1040 }
1041 }
1042 }
1043
GetMetaHandlesOfType(BaseTransaction * trans,ModelType type,std::vector<int64> * result)1044 void Directory::GetMetaHandlesOfType(BaseTransaction* trans,
1045 ModelType type,
1046 std::vector<int64>* result) {
1047 result->clear();
1048 ScopedKernelLock lock(this);
1049 for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
1050 it != kernel_->metahandles_map.end(); ++it) {
1051 EntryKernel* entry = it->second;
1052 const ModelType entry_type =
1053 GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
1054 if (entry_type == type)
1055 result->push_back(it->first);
1056 }
1057 }
1058
CollectMetaHandleCounts(std::vector<int> * num_entries_by_type,std::vector<int> * num_to_delete_entries_by_type)1059 void Directory::CollectMetaHandleCounts(
1060 std::vector<int>* num_entries_by_type,
1061 std::vector<int>* num_to_delete_entries_by_type) {
1062 syncable::ReadTransaction trans(FROM_HERE, this);
1063 ScopedKernelLock lock(this);
1064
1065 for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
1066 it != kernel_->metahandles_map.end(); ++it) {
1067 EntryKernel* entry = it->second;
1068 const ModelType type = GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
1069 (*num_entries_by_type)[type]++;
1070 if (entry->ref(IS_DEL))
1071 (*num_to_delete_entries_by_type)[type]++;
1072 }
1073 }
1074
GetNodeDetailsForType(BaseTransaction * trans,ModelType type)1075 scoped_ptr<base::ListValue> Directory::GetNodeDetailsForType(
1076 BaseTransaction* trans,
1077 ModelType type) {
1078 scoped_ptr<base::ListValue> nodes(new base::ListValue());
1079
1080 ScopedKernelLock lock(this);
1081 for (MetahandlesMap::iterator it = kernel_->metahandles_map.begin();
1082 it != kernel_->metahandles_map.end(); ++it) {
1083 if (GetModelTypeFromSpecifics(it->second->ref(SPECIFICS)) != type) {
1084 continue;
1085 }
1086
1087 EntryKernel* kernel = it->second;
1088 scoped_ptr<base::DictionaryValue> node(
1089 kernel->ToValue(GetCryptographer(trans)));
1090
1091 // Add the position index if appropriate. This must be done here (and not
1092 // in EntryKernel) because the EntryKernel does not have access to its
1093 // siblings.
1094 if (kernel->ShouldMaintainPosition() && !kernel->ref(IS_DEL)) {
1095 node->SetInteger("positionIndex", GetPositionIndex(trans, kernel));
1096 }
1097
1098 nodes->Append(node.release());
1099 }
1100
1101 return nodes.Pass();
1102 }
1103
CheckInvariantsOnTransactionClose(syncable::BaseTransaction * trans,const MetahandleSet & modified_handles)1104 bool Directory::CheckInvariantsOnTransactionClose(
1105 syncable::BaseTransaction* trans,
1106 const MetahandleSet& modified_handles) {
1107 // NOTE: The trans may be in the process of being destructed. Be careful if
1108 // you wish to call any of its virtual methods.
1109 switch (invariant_check_level_) {
1110 case FULL_DB_VERIFICATION: {
1111 MetahandleSet all_handles;
1112 GetAllMetaHandles(trans, &all_handles);
1113 return CheckTreeInvariants(trans, all_handles);
1114 }
1115 case VERIFY_CHANGES: {
1116 return CheckTreeInvariants(trans, modified_handles);
1117 }
1118 case OFF: {
1119 return true;
1120 }
1121 }
1122 NOTREACHED();
1123 return false;
1124 }
1125
FullyCheckTreeInvariants(syncable::BaseTransaction * trans)1126 bool Directory::FullyCheckTreeInvariants(syncable::BaseTransaction* trans) {
1127 MetahandleSet handles;
1128 GetAllMetaHandles(trans, &handles);
1129 return CheckTreeInvariants(trans, handles);
1130 }
1131
CheckTreeInvariants(syncable::BaseTransaction * trans,const MetahandleSet & handles)1132 bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
1133 const MetahandleSet& handles) {
1134 MetahandleSet::const_iterator i;
1135 for (i = handles.begin() ; i != handles.end() ; ++i) {
1136 int64 metahandle = *i;
1137 Entry e(trans, GET_BY_HANDLE, metahandle);
1138 if (!SyncAssert(e.good(), FROM_HERE, "Entry is bad", trans))
1139 return false;
1140 syncable::Id id = e.GetId();
1141 syncable::Id parentid = e.GetParentId();
1142
1143 if (id.IsRoot()) {
1144 if (!SyncAssert(e.GetIsDir(), FROM_HERE,
1145 "Entry should be a directory",
1146 trans))
1147 return false;
1148 if (!SyncAssert(parentid.IsRoot(), FROM_HERE,
1149 "Entry should be root",
1150 trans))
1151 return false;
1152 if (!SyncAssert(!e.GetIsUnsynced(), FROM_HERE,
1153 "Entry should be sycned",
1154 trans))
1155 return false;
1156 continue;
1157 }
1158
1159 if (!e.GetIsDel()) {
1160 if (!SyncAssert(id != parentid, FROM_HERE,
1161 "Id should be different from parent id.",
1162 trans))
1163 return false;
1164 if (!SyncAssert(!e.GetNonUniqueName().empty(), FROM_HERE,
1165 "Non unique name should not be empty.",
1166 trans))
1167 return false;
1168 int safety_count = handles.size() + 1;
1169 while (!parentid.IsRoot()) {
1170 Entry parent(trans, GET_BY_ID, parentid);
1171 if (!SyncAssert(parent.good(), FROM_HERE,
1172 "Parent entry is not valid.",
1173 trans))
1174 return false;
1175 if (handles.end() == handles.find(parent.GetMetahandle()))
1176 break; // Skip further checking if parent was unmodified.
1177 if (!SyncAssert(parent.GetIsDir(), FROM_HERE,
1178 "Parent should be a directory",
1179 trans))
1180 return false;
1181 if (!SyncAssert(!parent.GetIsDel(), FROM_HERE,
1182 "Parent should not have been marked for deletion.",
1183 trans))
1184 return false;
1185 if (!SyncAssert(handles.end() != handles.find(parent.GetMetahandle()),
1186 FROM_HERE,
1187 "Parent should be in the index.",
1188 trans))
1189 return false;
1190 parentid = parent.GetParentId();
1191 if (!SyncAssert(--safety_count > 0, FROM_HERE,
1192 "Count should be greater than zero.",
1193 trans))
1194 return false;
1195 }
1196 }
1197 int64 base_version = e.GetBaseVersion();
1198 int64 server_version = e.GetServerVersion();
1199 bool using_unique_client_tag = !e.GetUniqueClientTag().empty();
1200 if (CHANGES_VERSION == base_version || 0 == base_version) {
1201 if (e.GetIsUnappliedUpdate()) {
1202 // Must be a new item, or a de-duplicated unique client tag
1203 // that was created both locally and remotely.
1204 if (!using_unique_client_tag) {
1205 if (!SyncAssert(e.GetIsDel(), FROM_HERE,
1206 "The entry should not have been deleted.",
1207 trans))
1208 return false;
1209 }
1210 // It came from the server, so it must have a server ID.
1211 if (!SyncAssert(id.ServerKnows(), FROM_HERE,
1212 "The id should be from a server.",
1213 trans))
1214 return false;
1215 } else {
1216 if (e.GetIsDir()) {
1217 // TODO(chron): Implement this mode if clients ever need it.
1218 // For now, you can't combine a client tag and a directory.
1219 if (!SyncAssert(!using_unique_client_tag, FROM_HERE,
1220 "Directory cannot have a client tag.",
1221 trans))
1222 return false;
1223 }
1224 // Should be an uncomitted item, or a successfully deleted one.
1225 if (!e.GetIsDel()) {
1226 if (!SyncAssert(e.GetIsUnsynced(), FROM_HERE,
1227 "The item should be unsynced.",
1228 trans))
1229 return false;
1230 }
1231 // If the next check failed, it would imply that an item exists
1232 // on the server, isn't waiting for application locally, but either
1233 // is an unsynced create or a sucessful delete in the local copy.
1234 // Either way, that's a mismatch.
1235 if (!SyncAssert(0 == server_version, FROM_HERE,
1236 "Server version should be zero.",
1237 trans))
1238 return false;
1239 // Items that aren't using the unique client tag should have a zero
1240 // base version only if they have a local ID. Items with unique client
1241 // tags are allowed to use the zero base version for undeletion and
1242 // de-duplication; the unique client tag trumps the server ID.
1243 if (!using_unique_client_tag) {
1244 if (!SyncAssert(!id.ServerKnows(), FROM_HERE,
1245 "Should be a client only id.",
1246 trans))
1247 return false;
1248 }
1249 }
1250 } else {
1251 if (!SyncAssert(id.ServerKnows(),
1252 FROM_HERE,
1253 "Should be a server id.",
1254 trans))
1255 return false;
1256 }
1257 // Server-unknown items that are locally deleted should not be sent up to
1258 // the server. They must be !IS_UNSYNCED.
1259 if (!SyncAssert(!(!id.ServerKnows() && e.GetIsDel() && e.GetIsUnsynced()),
1260 FROM_HERE,
1261 "Locally deleted item must not be unsynced.",
1262 trans)) {
1263 return false;
1264 }
1265 }
1266 return true;
1267 }
1268
SetInvariantCheckLevel(InvariantCheckLevel check_level)1269 void Directory::SetInvariantCheckLevel(InvariantCheckLevel check_level) {
1270 invariant_check_level_ = check_level;
1271 }
1272
NextMetahandle()1273 int64 Directory::NextMetahandle() {
1274 ScopedKernelLock lock(this);
1275 int64 metahandle = (kernel_->next_metahandle)++;
1276 return metahandle;
1277 }
1278
1279 // Always returns a client ID that is the string representation of a negative
1280 // number.
NextId()1281 Id Directory::NextId() {
1282 int64 result;
1283 {
1284 ScopedKernelLock lock(this);
1285 result = (kernel_->persisted_info.next_id)--;
1286 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1287 }
1288 DCHECK_LT(result, 0);
1289 return Id::CreateFromClientString(base::Int64ToString(result));
1290 }
1291
HasChildren(BaseTransaction * trans,const Id & id)1292 bool Directory::HasChildren(BaseTransaction* trans, const Id& id) {
1293 ScopedKernelLock lock(this);
1294 return kernel_->parent_child_index.GetChildren(id) != NULL;
1295 }
1296
GetFirstChildId(BaseTransaction * trans,const EntryKernel * parent)1297 Id Directory::GetFirstChildId(BaseTransaction* trans,
1298 const EntryKernel* parent) {
1299 DCHECK(parent);
1300 DCHECK(parent->ref(IS_DIR));
1301
1302 ScopedKernelLock lock(this);
1303 const OrderedChildSet* children =
1304 kernel_->parent_child_index.GetChildren(parent->ref(ID));
1305
1306 // We're expected to return root if there are no children.
1307 if (!children)
1308 return Id();
1309
1310 return (*children->begin())->ref(ID);
1311 }
1312
GetPredecessorId(EntryKernel * e)1313 syncable::Id Directory::GetPredecessorId(EntryKernel* e) {
1314 ScopedKernelLock lock(this);
1315
1316 DCHECK(ParentChildIndex::ShouldInclude(e));
1317 const OrderedChildSet* children =
1318 kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
1319 DCHECK(children && !children->empty());
1320 OrderedChildSet::const_iterator i = children->find(e);
1321 DCHECK(i != children->end());
1322
1323 if (i == children->begin()) {
1324 return Id();
1325 } else {
1326 i--;
1327 return (*i)->ref(ID);
1328 }
1329 }
1330
GetSuccessorId(EntryKernel * e)1331 syncable::Id Directory::GetSuccessorId(EntryKernel* e) {
1332 ScopedKernelLock lock(this);
1333
1334 DCHECK(ParentChildIndex::ShouldInclude(e));
1335 const OrderedChildSet* children =
1336 kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
1337 DCHECK(children && !children->empty());
1338 OrderedChildSet::const_iterator i = children->find(e);
1339 DCHECK(i != children->end());
1340
1341 i++;
1342 if (i == children->end()) {
1343 return Id();
1344 } else {
1345 return (*i)->ref(ID);
1346 }
1347 }
1348
1349 // TODO(rlarocque): Remove all support for placing ShouldMaintainPosition()
1350 // items as siblings of items that do not maintain postions. It is required
1351 // only for tests. See crbug.com/178282.
PutPredecessor(EntryKernel * e,EntryKernel * predecessor)1352 void Directory::PutPredecessor(EntryKernel* e, EntryKernel* predecessor) {
1353 DCHECK(!e->ref(IS_DEL));
1354 if (!e->ShouldMaintainPosition()) {
1355 DCHECK(!e->ref(UNIQUE_POSITION).IsValid());
1356 return;
1357 }
1358 std::string suffix = e->ref(UNIQUE_BOOKMARK_TAG);
1359 DCHECK(!suffix.empty());
1360
1361 // Remove our item from the ParentChildIndex and remember to re-add it later.
1362 ScopedKernelLock lock(this);
1363 ScopedParentChildIndexUpdater updater(lock, e, &kernel_->parent_child_index);
1364
1365 // Note: The ScopedParentChildIndexUpdater will update this set for us as we
1366 // leave this function.
1367 const OrderedChildSet* siblings =
1368 kernel_->parent_child_index.GetChildren(e->ref(PARENT_ID));
1369
1370 if (!siblings) {
1371 // This parent currently has no other children.
1372 DCHECK(predecessor->ref(ID).IsRoot());
1373 UniquePosition pos = UniquePosition::InitialPosition(suffix);
1374 e->put(UNIQUE_POSITION, pos);
1375 return;
1376 }
1377
1378 if (predecessor->ref(ID).IsRoot()) {
1379 // We have at least one sibling, and we're inserting to the left of them.
1380 UniquePosition successor_pos = (*siblings->begin())->ref(UNIQUE_POSITION);
1381
1382 UniquePosition pos;
1383 if (!successor_pos.IsValid()) {
1384 // If all our successors are of non-positionable types, just create an
1385 // initial position. We arbitrarily choose to sort invalid positions to
1386 // the right of the valid positions.
1387 //
1388 // We really shouldn't need to support this. See TODO above.
1389 pos = UniquePosition::InitialPosition(suffix);
1390 } else {
1391 DCHECK(!siblings->empty());
1392 pos = UniquePosition::Before(successor_pos, suffix);
1393 }
1394
1395 e->put(UNIQUE_POSITION, pos);
1396 return;
1397 }
1398
1399 // We can't support placing an item after an invalid position. Fortunately,
1400 // the tests don't exercise this particular case. We should not support
1401 // siblings with invalid positions at all. See TODO above.
1402 DCHECK(predecessor->ref(UNIQUE_POSITION).IsValid());
1403
1404 OrderedChildSet::const_iterator neighbour = siblings->find(predecessor);
1405 DCHECK(neighbour != siblings->end());
1406
1407 ++neighbour;
1408 if (neighbour == siblings->end()) {
1409 // Inserting at the end of the list.
1410 UniquePosition pos = UniquePosition::After(
1411 predecessor->ref(UNIQUE_POSITION),
1412 suffix);
1413 e->put(UNIQUE_POSITION, pos);
1414 return;
1415 }
1416
1417 EntryKernel* successor = *neighbour;
1418
1419 // Another mixed valid and invalid position case. This one could be supported
1420 // in theory, but we're trying to deprecate support for siblings with and
1421 // without valid positions. See TODO above.
1422 DCHECK(successor->ref(UNIQUE_POSITION).IsValid());
1423
1424 // Finally, the normal case: inserting between two elements.
1425 UniquePosition pos = UniquePosition::Between(
1426 predecessor->ref(UNIQUE_POSITION),
1427 successor->ref(UNIQUE_POSITION),
1428 suffix);
1429 e->put(UNIQUE_POSITION, pos);
1430 return;
1431 }
1432
1433 // TODO(rlarocque): Avoid this indirection. Just return the set.
AppendChildHandles(const ScopedKernelLock & lock,const Id & parent_id,Directory::Metahandles * result)1434 void Directory::AppendChildHandles(const ScopedKernelLock& lock,
1435 const Id& parent_id,
1436 Directory::Metahandles* result) {
1437 const OrderedChildSet* children =
1438 kernel_->parent_child_index.GetChildren(parent_id);
1439 if (!children)
1440 return;
1441
1442 for (OrderedChildSet::const_iterator i = children->begin();
1443 i != children->end(); ++i) {
1444 DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID));
1445 result->push_back((*i)->ref(META_HANDLE));
1446 }
1447 }
1448
UnmarkDirtyEntry(WriteTransaction * trans,Entry * entry)1449 void Directory::UnmarkDirtyEntry(WriteTransaction* trans, Entry* entry) {
1450 CHECK(trans);
1451 entry->kernel_->clear_dirty(&kernel_->dirty_metahandles);
1452 }
1453
1454 } // namespace syncable
1455 } // namespace syncer
1456