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 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 48 Directory::PersistedKernelInfo::~PersistedKernelInfo() {} 49 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 61 Directory::SaveChangesSnapshot::SaveChangesSnapshot() 62 : kernel_info_status(KERNEL_SHARE_INFO_INVALID) { 63 } 64 65 Directory::SaveChangesSnapshot::~SaveChangesSnapshot() { 66 STLDeleteElements(&dirty_metas); 67 STLDeleteElements(&delete_journals); 68 } 69 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 86 Directory::Kernel::~Kernel() { 87 STLDeleteContainerPairSecondPointers(metahandles_map.begin(), 88 metahandles_map.end()); 89 } 90 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 108 Directory::~Directory() { 109 Close(); 110 } 111 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 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 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 196 DeleteJournal* Directory::delete_journal() { 197 DCHECK(delete_journal_.get()); 198 return delete_journal_.get(); 199 } 200 201 void Directory::Close() { 202 store_.reset(); 203 if (kernel_) { 204 delete kernel_; 205 kernel_ = NULL; 206 } 207 } 208 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 218 EntryKernel* Directory::GetEntryById(const Id& id) { 219 ScopedKernelLock lock(this); 220 return GetEntryById(id, &lock); 221 } 222 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 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 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 255 EntryKernel* Directory::GetEntryByHandle(int64 metahandle) { 256 ScopedKernelLock lock(this); 257 return GetEntryByHandle(metahandle, &lock); 258 } 259 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 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 285 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 309 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 325 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 335 bool Directory::InsertEntry(BaseWriteTransaction* trans, EntryKernel* entry) { 336 ScopedKernelLock lock(this); 337 return InsertEntry(trans, entry, &lock); 338 } 339 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 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 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 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 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 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 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 482 bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const { 483 DCHECK(trans != NULL); 484 return unrecoverable_error_set_; 485 } 486 487 void Directory::ClearDirtyMetahandles() { 488 kernel_->transaction_mutex.AssertAcquired(); 489 kernel_->dirty_metahandles.clear(); 490 } 491 492 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 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 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 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 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 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 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 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 841 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 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 880 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 888 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 896 size_t Directory::GetEntriesCount() const { 897 ScopedKernelLock lock(this); 898 return kernel_->metahandles_map.size(); 899 } 900 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 909 int64 Directory::GetTransactionVersion(ModelType type) const { 910 kernel_->transaction_mutex.AssertAcquired(); 911 return kernel_->persisted_info.transaction_version[type]; 912 } 913 914 void Directory::IncrementTransactionVersion(ModelType type) { 915 kernel_->transaction_mutex.AssertAcquired(); 916 kernel_->persisted_info.transaction_version[type]++; 917 } 918 919 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 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 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 947 bool Directory::InitialSyncEndedForType(ModelType type) { 948 syncable::ReadTransaction trans(FROM_HERE, this); 949 return InitialSyncEndedForType(&trans, type); 950 } 951 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 959 string Directory::store_birthday() const { 960 ScopedKernelLock lock(this); 961 return kernel_->persisted_info.store_birthday; 962 } 963 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 972 string Directory::bag_of_chips() const { 973 ScopedKernelLock lock(this); 974 return kernel_->persisted_info.bag_of_chips; 975 } 976 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 986 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 991 NigoriHandler* Directory::GetNigoriHandler() { 992 return nigori_handler_; 993 } 994 995 Cryptographer* Directory::GetCryptographer(const BaseTransaction* trans) { 996 DCHECK_EQ(this, trans->directory()); 997 return cryptographer_; 998 } 999 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 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 1018 int64 Directory::unsynced_entity_count() const { 1019 ScopedKernelLock lock(this); 1020 return kernel_->unsynced_metahandles.size(); 1021 } 1022 1023 bool Directory::TypeHasUnappliedUpdates(ModelType type) { 1024 ScopedKernelLock lock(this); 1025 return !kernel_->unapplied_update_metahandles[type].empty(); 1026 } 1027 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 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 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 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 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 1126 bool Directory::FullyCheckTreeInvariants(syncable::BaseTransaction* trans) { 1127 MetahandleSet handles; 1128 GetAllMetaHandles(trans, &handles); 1129 return CheckTreeInvariants(trans, handles); 1130 } 1131 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 1269 void Directory::SetInvariantCheckLevel(InvariantCheckLevel check_level) { 1270 invariant_check_level_ = check_level; 1271 } 1272 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. 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 1292 bool Directory::HasChildren(BaseTransaction* trans, const Id& id) { 1293 ScopedKernelLock lock(this); 1294 return kernel_->parent_child_index.GetChildren(id) != NULL; 1295 } 1296 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 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 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. 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. 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 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