metadata_database.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 6 7#include <algorithm> 8#include <stack> 9 10#include "base/bind.h" 11#include "base/callback.h" 12#include "base/files/file_path.h" 13#include "base/location.h" 14#include "base/memory/scoped_vector.h" 15#include "base/message_loop/message_loop_proxy.h" 16#include "base/sequenced_task_runner.h" 17#include "base/stl_util.h" 18#include "base/strings/string_number_conversions.h" 19#include "base/strings/string_util.h" 20#include "base/strings/stringprintf.h" 21#include "base/task_runner_util.h" 22#include "base/threading/thread_restrictions.h" 23#include "chrome/browser/drive/drive_api_util.h" 24#include "chrome/browser/google_apis/drive_api_parser.h" 25#include "chrome/browser/google_apis/drive_entry_kinds.h" 26#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 27#include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h" 28#include "chrome/browser/sync_file_system/logger.h" 29#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 30#include "third_party/leveldatabase/src/include/leveldb/db.h" 31#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 32#include "webkit/common/fileapi/file_system_util.h" 33 34namespace sync_file_system { 35namespace drive_backend { 36 37const char kDatabaseVersionKey[] = "VERSION"; 38const int64 kCurrentDatabaseVersion = 3; 39const char kServiceMetadataKey[] = "SERVICE"; 40const char kFileMetadataKeyPrefix[] = "FILE: "; 41const char kFileTrackerKeyPrefix[] = "TRACKER: "; 42 43struct DatabaseContents { 44 scoped_ptr<ServiceMetadata> service_metadata; 45 ScopedVector<FileMetadata> file_metadata; 46 ScopedVector<FileTracker> file_trackers; 47}; 48 49namespace { 50 51typedef MetadataDatabase::FileByID FileByID; 52typedef MetadataDatabase::TrackerByID TrackerByID; 53typedef MetadataDatabase::TrackersByParentAndTitle TrackersByParentAndTitle; 54typedef MetadataDatabase::TrackersByTitle TrackersByTitle; 55 56bool IsAppRoot(const FileTracker& tracker) { 57 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT || 58 tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 59} 60 61std::string RemovePrefix(const std::string& str, const std::string& prefix) { 62 if (StartsWithASCII(str, prefix, true)) 63 return str.substr(prefix.size()); 64 return str; 65} 66 67base::FilePath ReverseConcatPathComponents( 68 const std::vector<base::FilePath>& components) { 69 if (components.empty()) 70 return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators(); 71 72 size_t total_size = 0; 73 typedef std::vector<base::FilePath> PathComponents; 74 for (PathComponents::const_iterator itr = components.begin(); 75 itr != components.end(); ++itr) 76 total_size += itr->value().size() + 1; 77 78 base::FilePath::StringType result; 79 result.reserve(total_size); 80 for (PathComponents::const_reverse_iterator itr = components.rbegin(); 81 itr != components.rend(); ++itr) { 82 result.append(1, base::FilePath::kSeparators[0]); 83 result.append(itr->value()); 84 } 85 86 return base::FilePath(result).NormalizePathSeparators(); 87} 88 89void PopulateFileDetailsByFileResource( 90 const google_apis::FileResource& file_resource, 91 FileDetails* details) { 92 details->clear_parent_folder_ids(); 93 for (ScopedVector<google_apis::ParentReference>::const_iterator itr = 94 file_resource.parents().begin(); 95 itr != file_resource.parents().end(); 96 ++itr) { 97 details->add_parent_folder_ids((*itr)->file_id()); 98 } 99 details->set_title(file_resource.title()); 100 101 google_apis::DriveEntryKind kind = drive::util::GetKind(file_resource); 102 if (kind == google_apis::ENTRY_KIND_FILE) 103 details->set_file_kind(FILE_KIND_FILE); 104 else if (kind == google_apis::ENTRY_KIND_FOLDER) 105 details->set_file_kind(FILE_KIND_FOLDER); 106 else 107 details->set_file_kind(FILE_KIND_UNSUPPORTED); 108 109 details->set_md5(file_resource.md5_checksum()); 110 details->set_etag(file_resource.etag()); 111 details->set_creation_time(file_resource.created_date().ToInternalValue()); 112 details->set_modification_time( 113 file_resource.modified_date().ToInternalValue()); 114 details->set_deleted(false); 115} 116 117scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource( 118 int64 change_id, 119 const google_apis::FileResource& resource) { 120 scoped_ptr<FileMetadata> file(new FileMetadata); 121 file->set_file_id(resource.file_id()); 122 123 FileDetails* details = file->mutable_details(); 124 details->set_change_id(change_id); 125 126 if (resource.labels().is_trashed()) { 127 details->set_deleted(true); 128 return file.Pass(); 129 } 130 131 PopulateFileDetailsByFileResource(resource, details); 132 return file.Pass(); 133} 134 135scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource( 136 const google_apis::ChangeResource& change) { 137 scoped_ptr<FileMetadata> file(new FileMetadata); 138 file->set_file_id(change.file_id()); 139 140 FileDetails* details = file->mutable_details(); 141 details->set_change_id(change.change_id()); 142 143 if (change.is_deleted()) { 144 details->set_deleted(true); 145 return file.Pass(); 146 } 147 148 PopulateFileDetailsByFileResource(*change.file(), details); 149 return file.Pass(); 150} 151 152void CreateInitialSyncRootTracker( 153 int64 tracker_id, 154 const google_apis::FileResource& file_resource, 155 scoped_ptr<FileMetadata>* file_out, 156 scoped_ptr<FileTracker>* tracker_out) { 157 FileDetails details; 158 PopulateFileDetailsByFileResource(file_resource, &details); 159 160 scoped_ptr<FileMetadata> file(new FileMetadata); 161 file->set_file_id(file_resource.file_id()); 162 *file->mutable_details() = details; 163 164 scoped_ptr<FileTracker> tracker(new FileTracker); 165 tracker->set_tracker_id(tracker_id); 166 tracker->set_file_id(file_resource.file_id()); 167 tracker->set_parent_tracker_id(0); 168 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 169 tracker->set_dirty(false); 170 tracker->set_active(true); 171 tracker->set_needs_folder_listing(false); 172 *tracker->mutable_synced_details() = details; 173 174 *file_out = file.Pass(); 175 *tracker_out = tracker.Pass(); 176} 177 178void CreateInitialAppRootTracker( 179 int64 tracker_id, 180 const FileTracker& parent_tracker, 181 const google_apis::FileResource& file_resource, 182 scoped_ptr<FileMetadata>* file_out, 183 scoped_ptr<FileTracker>* tracker_out) { 184 FileDetails details; 185 PopulateFileDetailsByFileResource(file_resource, &details); 186 187 scoped_ptr<FileMetadata> file(new FileMetadata); 188 file->set_file_id(file_resource.file_id()); 189 *file->mutable_details() = details; 190 191 scoped_ptr<FileTracker> tracker(new FileTracker); 192 tracker->set_tracker_id(tracker_id); 193 tracker->set_parent_tracker_id(parent_tracker.tracker_id()); 194 tracker->set_file_id(file_resource.file_id()); 195 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 196 tracker->set_dirty(false); 197 tracker->set_active(false); 198 tracker->set_needs_folder_listing(false); 199 *tracker->mutable_synced_details() = details; 200 201 *file_out = file.Pass(); 202 *tracker_out = tracker.Pass(); 203} 204 205void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback, 206 const leveldb::Status& status) { 207 callback.Run(LevelDBStatusToSyncStatusCode(status)); 208} 209 210void PutServiceMetadataToBatch(const ServiceMetadata& service_metadata, 211 leveldb::WriteBatch* batch) { 212 std::string value; 213 bool success = service_metadata.SerializeToString(&value); 214 DCHECK(success); 215 batch->Put(kServiceMetadataKey, value); 216} 217 218void PutFileToBatch(const FileMetadata& file, leveldb::WriteBatch* batch) { 219 std::string value; 220 bool success = file.SerializeToString(&value); 221 DCHECK(success); 222 batch->Put(kFileMetadataKeyPrefix + file.file_id(), value); 223} 224 225void PutTrackerToBatch(const FileTracker& tracker, leveldb::WriteBatch* batch) { 226 std::string value; 227 bool success = tracker.SerializeToString(&value); 228 DCHECK(success); 229 batch->Put(kFileTrackerKeyPrefix + base::Int64ToString(tracker.tracker_id()), 230 value); 231} 232 233void PutFileDeletionToBatch(const std::string& file_id, 234 leveldb::WriteBatch* batch) { 235 batch->Delete(kFileMetadataKeyPrefix + file_id); 236} 237 238void PutTrackerDeletionToBatch(int64 tracker_id, leveldb::WriteBatch* batch) { 239 batch->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id)); 240} 241 242template <typename OutputIterator> 243OutputIterator PushChildTrackersToContainer( 244 const TrackersByParentAndTitle& trackers_by_parent, 245 int64 parent_tracker_id, 246 OutputIterator target_itr) { 247 TrackersByParentAndTitle::const_iterator found = 248 trackers_by_parent.find(parent_tracker_id); 249 if (found == trackers_by_parent.end()) 250 return target_itr; 251 252 for (TrackersByTitle::const_iterator title_itr = found->second.begin(); 253 title_itr != found->second.end(); ++title_itr) { 254 const TrackerSet& trackers = title_itr->second; 255 for (TrackerSet::const_iterator tracker_itr = trackers.begin(); 256 tracker_itr != trackers.end(); ++tracker_itr) { 257 *target_itr = (*tracker_itr)->tracker_id(); 258 ++target_itr; 259 } 260 } 261 return target_itr; 262} 263 264std::string GetTrackerTitle(const FileTracker& tracker) { 265 if (tracker.has_synced_details()) 266 return tracker.synced_details().title(); 267 return std::string(); 268} 269 270// Returns true if |db| has no content. 271bool IsDatabaseEmpty(leveldb::DB* db) { 272 DCHECK(db); 273 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 274 itr->SeekToFirst(); 275 return !itr->Valid(); 276} 277 278SyncStatusCode OpenDatabase(const base::FilePath& path, 279 scoped_ptr<leveldb::DB>* db_out, 280 bool* created) { 281 base::ThreadRestrictions::AssertIOAllowed(); 282 DCHECK(db_out); 283 DCHECK(created); 284 285 leveldb::Options options; 286 options.max_open_files = 0; // Use minimum. 287 options.create_if_missing = true; 288 leveldb::DB* db = NULL; 289 leveldb::Status db_status = 290 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db); 291 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status); 292 if (status != SYNC_STATUS_OK) { 293 delete db; 294 return status; 295 } 296 297 *created = IsDatabaseEmpty(db); 298 db_out->reset(db); 299 return status; 300} 301 302SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) { 303 base::ThreadRestrictions::AssertIOAllowed(); 304 DCHECK(db); 305 std::string value; 306 leveldb::Status status = 307 db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); 308 int64 version = 0; 309 if (status.ok()) { 310 if (!base::StringToInt64(value, &version)) 311 return SYNC_DATABASE_ERROR_FAILED; 312 } else { 313 if (!status.IsNotFound()) 314 return SYNC_DATABASE_ERROR_FAILED; 315 } 316 317 switch (version) { 318 case 0: 319 drive_backend::MigrateDatabaseFromV0ToV1(db); 320 // fall-through 321 case 1: 322 drive_backend::MigrateDatabaseFromV1ToV2(db); 323 // fall-through 324 case 2: 325 // TODO(tzik): Migrate database from version 2 to 3. 326 // * Add sync-root folder as active, dirty and needs_folder_listing 327 // folder. 328 // * Add app-root folders for each origins. Each app-root folder for 329 // an enabled origin should be a active, dirty and 330 // needs_folder_listing folder. And Each app-root folder for a 331 // disabled origin should be an inactive, dirty and 332 // non-needs_folder_listing folder. 333 // * Add a file metadata for each file in previous version. 334 NOTIMPLEMENTED(); 335 return SYNC_DATABASE_ERROR_FAILED; 336 // fall-through 337 case 3: 338 DCHECK_EQ(3, kCurrentDatabaseVersion); 339 return SYNC_STATUS_OK; 340 default: 341 return SYNC_DATABASE_ERROR_FAILED; 342 } 343} 344 345SyncStatusCode WriteVersionInfo(leveldb::DB* db) { 346 base::ThreadRestrictions::AssertIOAllowed(); 347 DCHECK(db); 348 return LevelDBStatusToSyncStatusCode( 349 db->Put(leveldb::WriteOptions(), 350 kDatabaseVersionKey, 351 base::Int64ToString(kCurrentDatabaseVersion))); 352} 353 354SyncStatusCode ReadDatabaseContents(leveldb::DB* db, 355 DatabaseContents* contents) { 356 base::ThreadRestrictions::AssertIOAllowed(); 357 DCHECK(db); 358 DCHECK(contents); 359 360 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 361 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) { 362 std::string key = itr->key().ToString(); 363 std::string value = itr->value().ToString(); 364 if (key == kServiceMetadataKey) { 365 scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata); 366 if (!service_metadata->ParseFromString(value)) { 367 util::Log(logging::LOG_WARNING, FROM_HERE, 368 "Failed to parse SyncServiceMetadata"); 369 continue; 370 } 371 372 contents->service_metadata = service_metadata.Pass(); 373 continue; 374 } 375 376 if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) { 377 std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix); 378 379 scoped_ptr<FileMetadata> file(new FileMetadata); 380 if (!file->ParseFromString(itr->value().ToString())) { 381 util::Log(logging::LOG_WARNING, FROM_HERE, 382 "Failed to parse a FileMetadata"); 383 continue; 384 } 385 386 contents->file_metadata.push_back(file.release()); 387 continue; 388 } 389 390 if (StartsWithASCII(key, kFileTrackerKeyPrefix, true)) { 391 int64 tracker_id = 0; 392 if (!base::StringToInt64(RemovePrefix(key, kFileTrackerKeyPrefix), 393 &tracker_id)) { 394 util::Log(logging::LOG_WARNING, FROM_HERE, 395 "Failed to parse TrackerID"); 396 continue; 397 } 398 399 scoped_ptr<FileTracker> tracker(new FileTracker); 400 if (!tracker->ParseFromString(itr->value().ToString())) { 401 util::Log(logging::LOG_WARNING, FROM_HERE, 402 "Failed to parse a Tracker"); 403 continue; 404 } 405 contents->file_trackers.push_back(tracker.release()); 406 continue; 407 } 408 } 409 410 return SYNC_STATUS_OK; 411} 412 413SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents, 414 leveldb::WriteBatch* batch) { 415 if (!contents->service_metadata) { 416 contents->service_metadata.reset(new ServiceMetadata); 417 contents->service_metadata->set_next_tracker_id(1); 418 419 std::string value; 420 contents->service_metadata->SerializeToString(&value); 421 batch->Put(kServiceMetadataKey, value); 422 } 423 return SYNC_STATUS_OK; 424} 425 426SyncStatusCode RemoveUnreachableItems(DatabaseContents* contents, 427 leveldb::WriteBatch* batch) { 428 TrackerByID unvisited_trackers; 429 typedef std::map<int64, std::set<FileTracker*> > TrackersByParent; 430 TrackersByParent trackers_by_parent; 431 432 for (ScopedVector<FileTracker>::iterator itr = 433 contents->file_trackers.begin(); 434 itr != contents->file_trackers.end(); 435 ++itr) { 436 FileTracker* tracker = *itr; 437 DCHECK(!ContainsKey(unvisited_trackers, tracker->tracker_id())); 438 unvisited_trackers[tracker->tracker_id()] = tracker; 439 if (tracker->parent_tracker_id()) 440 trackers_by_parent[tracker->parent_tracker_id()].insert(tracker); 441 } 442 443 // Traverse synced tracker tree. Take only active items, app-root and their 444 // children. Drop unreachable items. 445 ScopedVector<FileTracker> reachable_trackers; 446 std::stack<int64> pending; 447 if (contents->service_metadata->sync_root_tracker_id()) 448 pending.push(contents->service_metadata->sync_root_tracker_id()); 449 450 while (!pending.empty()) { 451 int64 tracker_id = pending.top(); 452 pending.pop(); 453 454 { 455 TrackerByID::iterator found = unvisited_trackers.find(tracker_id); 456 if (found == unvisited_trackers.end()) 457 continue; 458 459 FileTracker* tracker = found->second; 460 unvisited_trackers.erase(found); 461 reachable_trackers.push_back(tracker); 462 463 if (!tracker->active()) 464 continue; 465 } 466 467 TrackersByParent::iterator found = trackers_by_parent.find(tracker_id); 468 if (found == trackers_by_parent.end()) 469 continue; 470 471 for (std::set<FileTracker*>::const_iterator itr = 472 found->second.begin(); 473 itr != found->second.end(); 474 ++itr) 475 pending.push((*itr)->tracker_id()); 476 } 477 478 // Delete all unreachable trackers. 479 for (TrackerByID::iterator itr = unvisited_trackers.begin(); 480 itr != unvisited_trackers.end(); ++itr) { 481 FileTracker* tracker = itr->second; 482 PutTrackerDeletionToBatch(tracker->tracker_id(), batch); 483 delete tracker; 484 } 485 unvisited_trackers.clear(); 486 487 // |reachable_trackers| contains all files/folders reachable from sync-root 488 // folder via active folders and app-root folders. 489 // Update the tracker set in database contents with the reachable tracker set. 490 contents->file_trackers.weak_clear(); 491 contents->file_trackers.swap(reachable_trackers); 492 493 // Do the similar traverse for FileMetadata and remove FileMetadata that don't 494 // have reachable trackers. 495 FileByID unreferred_files; 496 for (ScopedVector<FileMetadata>::const_iterator itr = 497 contents->file_metadata.begin(); 498 itr != contents->file_metadata.end(); 499 ++itr) { 500 unreferred_files.insert(std::make_pair((*itr)->file_id(), *itr)); 501 } 502 503 ScopedVector<FileMetadata> referred_files; 504 for (ScopedVector<FileTracker>::const_iterator itr = 505 contents->file_trackers.begin(); 506 itr != contents->file_trackers.end(); 507 ++itr) { 508 FileByID::iterator found = unreferred_files.find((*itr)->file_id()); 509 if (found != unreferred_files.end()) { 510 referred_files.push_back(found->second); 511 unreferred_files.erase(found); 512 } 513 } 514 515 for (FileByID::iterator itr = unreferred_files.begin(); 516 itr != unreferred_files.end(); ++itr) { 517 FileMetadata* file = itr->second; 518 PutFileDeletionToBatch(file->file_id(), batch); 519 delete file; 520 } 521 unreferred_files.clear(); 522 523 contents->file_metadata.weak_clear(); 524 contents->file_metadata.swap(referred_files); 525 526 return SYNC_STATUS_OK; 527} 528 529template <typename Container, typename Key, typename Value> 530bool FindItem(const Container& container, const Key& key, Value* value) { 531 typename Container::const_iterator found = container.find(key); 532 if (found == container.end()) 533 return false; 534 if (value) 535 *value = *found->second; 536 return true; 537} 538 539template <typename Container, typename Key> 540typename Container::mapped_type FindAndEraseItem(Container* container, 541 const Key& key) { 542 typedef typename Container::mapped_type Value; 543 typename Container::iterator found = container->find(key); 544 if (found == container->end()) 545 return Value(); 546 547 Value result = found->second; 548 container->erase(found); 549 return result; 550} 551 552void RunSoon(const tracked_objects::Location& from_here, 553 const base::Closure& closure) { 554 base::MessageLoopProxy::current()->PostTask(from_here, closure); 555} 556 557} // namespace 558 559bool MetadataDatabase::DirtyTrackerComparator::operator()( 560 const FileTracker* left, 561 const FileTracker* right) const { 562 return left->tracker_id() < right->tracker_id(); 563} 564 565// static 566void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner, 567 const base::FilePath& database_path, 568 const CreateCallback& callback) { 569 task_runner->PostTask(FROM_HERE, base::Bind( 570 &CreateOnTaskRunner, 571 base::MessageLoopProxy::current(), 572 make_scoped_refptr(task_runner), 573 database_path, callback)); 574} 575 576MetadataDatabase::~MetadataDatabase() { 577 task_runner_->DeleteSoon(FROM_HERE, db_.release()); 578 STLDeleteContainerPairSecondPointers( 579 file_by_id_.begin(), file_by_id_.end()); 580 STLDeleteContainerPairSecondPointers( 581 tracker_by_id_.begin(), tracker_by_id_.end()); 582} 583 584int64 MetadataDatabase::GetLargestChangeID() const { 585 return service_metadata_->largest_change_id(); 586} 587 588int64 MetadataDatabase::GetSyncRootTrackerID() const { 589 return service_metadata_->sync_root_tracker_id(); 590} 591 592bool MetadataDatabase::HasSyncRoot() const { 593 return service_metadata_->has_sync_root_tracker_id() && 594 !!service_metadata_->sync_root_tracker_id(); 595} 596 597void MetadataDatabase::PopulateInitialData( 598 int64 largest_change_id, 599 const google_apis::FileResource& sync_root_folder, 600 const ScopedVector<google_apis::FileResource>& app_root_folders, 601 const SyncStatusCallback& callback) { 602 DCHECK(tracker_by_id_.empty()); 603 DCHECK(file_by_id_.empty()); 604 605 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 606 service_metadata_->set_largest_change_id(largest_change_id); 607 608 FileTracker* sync_root_tracker = NULL; 609 int64 sync_root_tracker_id = 0; 610 { 611 scoped_ptr<FileMetadata> folder; 612 scoped_ptr<FileTracker> tracker; 613 CreateInitialSyncRootTracker(GetNextTrackerID(batch.get()), 614 sync_root_folder, 615 &folder, 616 &tracker); 617 std::string sync_root_folder_id = folder->file_id(); 618 sync_root_tracker = tracker.get(); 619 sync_root_tracker_id = tracker->tracker_id(); 620 621 PutFileToBatch(*folder, batch.get()); 622 PutTrackerToBatch(*tracker, batch.get()); 623 624 service_metadata_->set_sync_root_tracker_id(tracker->tracker_id()); 625 PutServiceMetadataToBatch(*service_metadata_, batch.get()); 626 627 trackers_by_file_id_[folder->file_id()].Insert(tracker.get()); 628 629 file_by_id_[sync_root_folder_id] = folder.release(); 630 tracker_by_id_[sync_root_tracker_id] = tracker.release(); 631 } 632 633 for (ScopedVector<google_apis::FileResource>::const_iterator itr = 634 app_root_folders.begin(); 635 itr != app_root_folders.end(); 636 ++itr) { 637 const google_apis::FileResource& folder_resource = **itr; 638 scoped_ptr<FileMetadata> folder; 639 scoped_ptr<FileTracker> tracker; 640 CreateInitialAppRootTracker(GetNextTrackerID(batch.get()), 641 *sync_root_tracker, 642 folder_resource, 643 &folder, 644 &tracker); 645 std::string title = folder->details().title(); 646 std::string folder_id = folder->file_id(); 647 int64 tracker_id = tracker->tracker_id(); 648 649 PutFileToBatch(*folder, batch.get()); 650 PutTrackerToBatch(*tracker, batch.get()); 651 652 trackers_by_file_id_[folder_id].Insert(tracker.get()); 653 trackers_by_parent_and_title_[sync_root_tracker_id][title] 654 .Insert(tracker.get()); 655 656 file_by_id_[folder_id] = folder.release(); 657 tracker_by_id_[tracker_id] = tracker.release(); 658 } 659 660 WriteToDatabase(batch.Pass(), callback); 661} 662 663 664void MetadataDatabase::RegisterApp(const std::string& app_id, 665 const std::string& folder_id, 666 const SyncStatusCallback& callback) { 667 if (FindAppRootTracker(app_id, NULL)) { 668 // The app-root is already registered. 669 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 670 return; 671 } 672 673 TrackerSet trackers; 674 if (!FindTrackersByFileID(folder_id, &trackers) || trackers.has_active()) { 675 // The folder is tracked by another tracker. 676 util::Log(logging::LOG_WARNING, FROM_HERE, 677 "Failed to register App for %s", app_id.c_str()); 678 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_HAS_CONFLICT)); 679 return; 680 } 681 682 int64 sync_root_tracker_id = service_metadata_->sync_root_tracker_id(); 683 if (!sync_root_tracker_id) { 684 util::Log(logging::LOG_WARNING, FROM_HERE, 685 "Sync-root needs to be set up before registering app-root"); 686 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 687 return; 688 } 689 690 // Make this tracker an app-root tracker. 691 FileTracker* app_root_tracker = NULL; 692 for (TrackerSet::iterator itr = trackers.begin(); 693 itr != trackers.end(); ++itr) { 694 FileTracker* tracker = *itr; 695 if (tracker->parent_tracker_id() == sync_root_tracker_id) 696 app_root_tracker = tracker; 697 } 698 699 if (!app_root_tracker) { 700 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 701 return; 702 } 703 704 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 705 RegisterTrackerAsAppRoot(app_id, app_root_tracker->tracker_id(), batch.get()); 706 WriteToDatabase(batch.Pass(), callback); 707} 708 709void MetadataDatabase::DisableApp(const std::string& app_id, 710 const SyncStatusCallback& callback) { 711 FileTracker tracker; 712 if (!FindAppRootTracker(app_id, &tracker)) { 713 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 714 return; 715 } 716 717 if (tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) { 718 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 719 return; 720 } 721 722 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 723 MakeAppRootDisabled(tracker.tracker_id(), batch.get()); 724 WriteToDatabase(batch.Pass(), callback); 725} 726 727void MetadataDatabase::EnableApp(const std::string& app_id, 728 const SyncStatusCallback& callback) { 729 FileTracker tracker; 730 if (!FindAppRootTracker(app_id, &tracker) || 731 tracker.tracker_kind() == TRACKER_KIND_REGULAR) { 732 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 733 return; 734 } 735 736 if (tracker.tracker_kind() == TRACKER_KIND_APP_ROOT) { 737 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 738 return; 739 } 740 741 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 742 MakeAppRootEnabled(tracker.tracker_id(), batch.get()); 743 WriteToDatabase(batch.Pass(), callback); 744} 745 746void MetadataDatabase::UnregisterApp(const std::string& app_id, 747 const SyncStatusCallback& callback) { 748 FileTracker tracker; 749 if (!FindAppRootTracker(app_id, &tracker) || 750 tracker.tracker_kind() == TRACKER_KIND_REGULAR) { 751 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 752 return; 753 } 754 755 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 756 UnregisterTrackerAsAppRoot(app_id, batch.get()); 757 WriteToDatabase(batch.Pass(), callback); 758} 759 760bool MetadataDatabase::FindAppRootTracker(const std::string& app_id, 761 FileTracker* tracker) const { 762 return FindItem(app_root_by_app_id_, app_id, tracker); 763} 764 765bool MetadataDatabase::FindFileByFileID(const std::string& file_id, 766 FileMetadata* file) const { 767 return FindItem(file_by_id_, file_id, file); 768} 769 770bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id, 771 TrackerSet* trackers) const { 772 TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id); 773 if (found == trackers_by_file_id_.end()) 774 return false; 775 if (trackers) 776 *trackers = found->second; 777 return true; 778} 779 780bool MetadataDatabase::FindTrackersByParentAndTitle( 781 int64 parent_tracker_id, 782 const std::string& title, 783 TrackerSet* trackers) const { 784 TrackersByParentAndTitle::const_iterator found_by_parent = 785 trackers_by_parent_and_title_.find(parent_tracker_id); 786 if (found_by_parent == trackers_by_parent_and_title_.end()) 787 return false; 788 789 TrackersByTitle::const_iterator found_by_title = 790 found_by_parent->second.find(title); 791 if (found_by_title == found_by_parent->second.end()) 792 return false; 793 794 if (trackers) 795 *trackers = found_by_title->second; 796 return true; 797} 798 799bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id, 800 FileTracker* tracker) const { 801 return FindItem(tracker_by_id_, tracker_id, tracker); 802} 803 804bool MetadataDatabase::BuildPathForTracker(int64 tracker_id, 805 base::FilePath* path) const { 806 FileTracker current; 807 if (!FindTrackerByTrackerID(tracker_id, ¤t) || !current.active()) 808 return false; 809 810 std::vector<base::FilePath> components; 811 while (!IsAppRoot(current)) { 812 std::string title = GetTrackerTitle(current); 813 if (title.empty()) 814 return false; 815 components.push_back(base::FilePath::FromUTF8Unsafe(title)); 816 if (!FindTrackerByTrackerID(current.parent_tracker_id(), ¤t) || 817 !current.active()) 818 return false; 819 } 820 821 if (path) 822 *path = ReverseConcatPathComponents(components); 823 824 return true; 825} 826 827void MetadataDatabase::UpdateByChangeList( 828 ScopedVector<google_apis::ChangeResource> changes, 829 const SyncStatusCallback& callback) { 830 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 831 832 for (ScopedVector<google_apis::ChangeResource>::const_iterator itr = 833 changes.begin(); 834 itr != changes.end(); 835 ++itr) { 836 const google_apis::ChangeResource& change = **itr; 837 scoped_ptr<FileMetadata> file( 838 CreateFileMetadataFromChangeResource(change)); 839 std::string file_id = file->file_id(); 840 841 MarkTrackersDirtyByFileID(file_id, batch.get()); 842 if (!file->details().deleted()) 843 MaybeAddTrackersForNewFile(*file, batch.get()); 844 845 if (FindTrackersByFileID(file_id, NULL)) { 846 PutFileToBatch(*file, batch.get()); 847 848 // Set |file| to |file_by_id_[file_id]| and delete old value. 849 FileMetadata* file_ptr = file.release(); 850 std::swap(file_ptr, file_by_id_[file_id]); 851 delete file_ptr; 852 } 853 } 854 855 WriteToDatabase(batch.Pass(), callback); 856} 857 858void MetadataDatabase::UpdateByFileResource( 859 int64 change_id, 860 const google_apis::FileResource& resource, 861 const SyncStatusCallback& callback) { 862 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 863 864 scoped_ptr<FileMetadata> file( 865 CreateFileMetadataFromFileResource(change_id, resource)); 866 std::string file_id = file->file_id(); 867 868 // TODO(tzik): Consolidate with UpdateByChangeList. 869 MarkTrackersDirtyByFileID(file_id, batch.get()); 870 if (!file->details().deleted()) { 871 MaybeAddTrackersForNewFile(*file, batch.get()); 872 873 if (FindTrackersByFileID(file_id, NULL)) { 874 PutFileToBatch(*file, batch.get()); 875 876 // Set |file| to |file_by_id_[file_id]| and delete old value. 877 FileMetadata* file_ptr = file.release(); 878 std::swap(file_ptr, file_by_id_[file_id]); 879 delete file_ptr; 880 } 881 } 882 883 WriteToDatabase(batch.Pass(), callback); 884} 885 886void MetadataDatabase::PopulateFolderByChildList( 887 const std::string& folder_id, 888 const FileIDList& child_file_ids, 889 const SyncStatusCallback& callback) { 890 TrackerSet trackers; 891 if (!FindTrackersByFileID(folder_id, &trackers) || 892 !trackers.has_active()) { 893 // It's OK that there is no folder to populate its children. 894 // Inactive folders should ignore their contents updates. 895 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 896 return; 897 } 898 899 FileTracker* folder_tracker = 900 tracker_by_id_[trackers.active_tracker()->tracker_id()]; 901 DCHECK(folder_tracker); 902 std::set<std::string> children(child_file_ids.begin(), child_file_ids.end()); 903 904 std::vector<int64> known_children; 905 PushChildTrackersToContainer(trackers_by_parent_and_title_, 906 folder_tracker->tracker_id(), 907 std::back_inserter(known_children)); 908 for (std::vector<int64>::iterator itr = known_children.begin(); 909 itr != known_children.end(); ++itr) 910 children.erase(tracker_by_id_[*itr]->file_id()); 911 912 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 913 for (FileIDList::const_iterator itr = child_file_ids.begin(); 914 itr != child_file_ids.end(); ++itr) 915 CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get()); 916 folder_tracker->set_needs_folder_listing(false); 917 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker)) { 918 folder_tracker->set_dirty(false); 919 dirty_trackers_.erase(folder_tracker); 920 } 921 PutTrackerToBatch(*folder_tracker, batch.get()); 922 923 WriteToDatabase(batch.Pass(), callback); 924} 925 926void MetadataDatabase::UpdateTracker(int64 tracker_id, 927 const FileDetails& updated_details, 928 const SyncStatusCallback& callback) { 929 TrackerByID::iterator found = tracker_by_id_.find(tracker_id); 930 if (found == tracker_by_id_.end()) { 931 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 932 return; 933 } 934 935 FileTracker* tracker = found->second; 936 DCHECK(tracker); 937 938 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 939 940 if (updated_details.deleted()) { 941 // The update deletes the local file. 942 FileByID::iterator found = file_by_id_.find(tracker->file_id()); 943 if (found == file_by_id_.end() || found->second->details().deleted()) { 944 // Both the tracker and metadata have the deleted flag, now it's safe to 945 // delete the |tracker|. 946 RemoveTracker(tracker->tracker_id(), batch.get()); 947 } else { 948 // The local file is deleted, but corresponding remote file isn't. 949 // Put the tracker back to the initial state. 950 tracker->clear_synced_details(); 951 tracker->set_dirty(true); 952 tracker->set_active(false); 953 PutTrackerToBatch(*tracker, batch.get()); 954 } 955 956 WriteToDatabase(batch.Pass(), callback); 957 return; 958 } 959 960 // Check if the tracker was retitled. If it was, update the title and its 961 // index in advance. 962 if (!tracker->has_synced_details() || 963 tracker->synced_details().title() != updated_details.title()) { 964 UpdateTrackerTitle(tracker, updated_details.title(), batch.get()); 965 } 966 967 *tracker->mutable_synced_details() = updated_details; 968 969 // Activate the tracker if: 970 // - There is no active tracker that tracks |tracker->file_id()|. 971 // - There is no active tracker that has the same |parent| and |title|. 972 if (!tracker->active() && CanActivateTracker(*tracker)) 973 MakeTrackerActive(tracker->tracker_id(), batch.get()); 974 if (tracker->dirty() && !ShouldKeepDirty(*tracker)) { 975 tracker->set_dirty(false); 976 dirty_trackers_.erase(tracker); 977 } 978 PutTrackerToBatch(*tracker, batch.get()); 979 980 WriteToDatabase(batch.Pass(), callback); 981} 982 983MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner) 984 : task_runner_(task_runner), weak_ptr_factory_(this) { 985 DCHECK(task_runner); 986} 987 988// static 989void MetadataDatabase::CreateOnTaskRunner( 990 base::SingleThreadTaskRunner* callback_runner, 991 base::SequencedTaskRunner* task_runner, 992 const base::FilePath& database_path, 993 const CreateCallback& callback) { 994 scoped_ptr<MetadataDatabase> metadata_database( 995 new MetadataDatabase(task_runner)); 996 SyncStatusCode status = 997 metadata_database->InitializeOnTaskRunner(database_path); 998 if (status != SYNC_STATUS_OK) 999 metadata_database.reset(); 1000 1001 callback_runner->PostTask(FROM_HERE, base::Bind( 1002 callback, status, base::Passed(&metadata_database))); 1003} 1004 1005// static 1006SyncStatusCode MetadataDatabase::CreateForTesting( 1007 scoped_ptr<leveldb::DB> db, 1008 scoped_ptr<MetadataDatabase>* metadata_database_out) { 1009 scoped_ptr<MetadataDatabase> metadata_database( 1010 new MetadataDatabase(base::MessageLoopProxy::current())); 1011 metadata_database->db_ = db.Pass(); 1012 SyncStatusCode status = 1013 metadata_database->InitializeOnTaskRunner(base::FilePath()); 1014 if (status == SYNC_STATUS_OK) 1015 *metadata_database_out = metadata_database.Pass(); 1016 return status; 1017} 1018 1019SyncStatusCode MetadataDatabase::InitializeOnTaskRunner( 1020 const base::FilePath& database_path) { 1021 base::ThreadRestrictions::AssertIOAllowed(); 1022 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 1023 1024 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 1025 bool created = false; 1026 // Open database unless |db_| is overridden for testing. 1027 if (!db_) { 1028 status = OpenDatabase(database_path, &db_, &created); 1029 if (status != SYNC_STATUS_OK) 1030 return status; 1031 } 1032 1033 if (created) { 1034 status = WriteVersionInfo(db_.get()); 1035 if (status != SYNC_STATUS_OK) 1036 return status; 1037 } else { 1038 status = MigrateDatabaseIfNeeded(db_.get()); 1039 if (status != SYNC_STATUS_OK) 1040 return status; 1041 } 1042 1043 DatabaseContents contents; 1044 status = ReadDatabaseContents(db_.get(), &contents); 1045 if (status != SYNC_STATUS_OK) 1046 return status; 1047 1048 leveldb::WriteBatch batch; 1049 status = InitializeServiceMetadata(&contents, &batch); 1050 if (status != SYNC_STATUS_OK) 1051 return status; 1052 1053 status = RemoveUnreachableItems(&contents, &batch); 1054 if (status != SYNC_STATUS_OK) 1055 return status; 1056 1057 status = LevelDBStatusToSyncStatusCode( 1058 db_->Write(leveldb::WriteOptions(), &batch)); 1059 if (status != SYNC_STATUS_OK) 1060 return status; 1061 1062 BuildIndexes(&contents); 1063 return status; 1064} 1065 1066void MetadataDatabase::BuildIndexes(DatabaseContents* contents) { 1067 service_metadata_ = contents->service_metadata.Pass(); 1068 1069 for (ScopedVector<FileMetadata>::const_iterator itr = 1070 contents->file_metadata.begin(); 1071 itr != contents->file_metadata.end(); 1072 ++itr) { 1073 file_by_id_[(*itr)->file_id()] = *itr; 1074 } 1075 contents->file_metadata.weak_clear(); 1076 1077 for (ScopedVector<FileTracker>::const_iterator itr = 1078 contents->file_trackers.begin(); 1079 itr != contents->file_trackers.end(); 1080 ++itr) { 1081 FileTracker* tracker = *itr; 1082 tracker_by_id_[tracker->tracker_id()] = tracker; 1083 trackers_by_file_id_[tracker->file_id()].Insert(tracker); 1084 1085 if (IsAppRoot(*tracker)) 1086 app_root_by_app_id_[tracker->app_id()] = tracker; 1087 1088 if (tracker->parent_tracker_id()) { 1089 std::string title = GetTrackerTitle(*tracker); 1090 TrackerSet* trackers = 1091 &trackers_by_parent_and_title_[tracker->parent_tracker_id()][title]; 1092 trackers->Insert(tracker); 1093 } 1094 1095 if (tracker->dirty()) 1096 dirty_trackers_.insert(tracker); 1097 } 1098 contents->file_trackers.weak_clear(); 1099} 1100 1101void MetadataDatabase::RegisterTrackerAsAppRoot( 1102 const std::string& app_id, 1103 int64 tracker_id, 1104 leveldb::WriteBatch* batch) { 1105 FileTracker* tracker = tracker_by_id_[tracker_id]; 1106 DCHECK(tracker); 1107 tracker->set_app_id(app_id); 1108 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1109 app_root_by_app_id_[app_id] = tracker; 1110 1111 MakeTrackerActive(tracker->tracker_id(), batch); 1112} 1113 1114void MetadataDatabase::UnregisterTrackerAsAppRoot( 1115 const std::string& app_id, 1116 leveldb::WriteBatch* batch) { 1117 FileTracker* tracker = FindAndEraseItem(&app_root_by_app_id_, app_id); 1118 tracker->set_app_id(std::string()); 1119 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1120 1121 // Inactivate the tracker to drop all descendant. 1122 // (Note that we set tracker_kind to TRACKER_KIND_REGULAR before calling 1123 // this.) 1124 MakeTrackerInactive(tracker->tracker_id(), batch); 1125} 1126 1127void MetadataDatabase::MakeTrackerActive(int64 tracker_id, 1128 leveldb::WriteBatch* batch) { 1129 FileTracker* tracker = tracker_by_id_[tracker_id]; 1130 DCHECK(tracker); 1131 DCHECK(!tracker->active()); 1132 1133 int64 parent_tracker_id = tracker->parent_tracker_id(); 1134 DCHECK(tracker->has_synced_details()); 1135 trackers_by_file_id_[tracker->file_id()].Activate(tracker); 1136 if (parent_tracker_id) { 1137 trackers_by_parent_and_title_[parent_tracker_id][ 1138 tracker->synced_details().title()].Activate(tracker); 1139 } 1140 tracker->set_active(true); 1141 tracker->set_needs_folder_listing( 1142 tracker->synced_details().file_kind() == FILE_KIND_FOLDER); 1143 tracker->set_dirty(true); 1144 dirty_trackers_.insert(tracker); 1145 1146 PutTrackerToBatch(*tracker, batch); 1147} 1148 1149void MetadataDatabase::MakeTrackerInactive(int64 tracker_id, 1150 leveldb::WriteBatch* batch) { 1151 FileTracker* tracker = tracker_by_id_[tracker_id]; 1152 DCHECK(tracker); 1153 DCHECK(tracker->active()); 1154 DCHECK_EQ(TRACKER_KIND_REGULAR, tracker->tracker_kind()); 1155 trackers_by_file_id_[tracker->file_id()].Inactivate(tracker); 1156 1157 std::string title = GetTrackerTitle(*tracker); 1158 int64 parent_tracker_id = tracker->parent_tracker_id(); 1159 if (parent_tracker_id) 1160 trackers_by_parent_and_title_[parent_tracker_id][title].Inactivate(tracker); 1161 tracker->set_active(false); 1162 1163 RemoveAllDescendantTrackers(tracker_id, batch); 1164 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1165 if (parent_tracker_id) 1166 MarkTrackersDirtyByPath(parent_tracker_id, title, batch); 1167 PutTrackerToBatch(*tracker, batch); 1168} 1169 1170void MetadataDatabase::MakeAppRootDisabled(int64 tracker_id, 1171 leveldb::WriteBatch* batch) { 1172 FileTracker* tracker = tracker_by_id_[tracker_id]; 1173 DCHECK(tracker); 1174 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind()); 1175 DCHECK(tracker->active()); 1176 1177 // Keep the app-root tracker active (but change the tracker_kind) so that 1178 // other conflicting trackers won't become active. 1179 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT); 1180 PutTrackerToBatch(*tracker, batch); 1181} 1182 1183void MetadataDatabase::MakeAppRootEnabled(int64 tracker_id, 1184 leveldb::WriteBatch* batch) { 1185 FileTracker* tracker = tracker_by_id_[tracker_id]; 1186 DCHECK(tracker); 1187 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind()); 1188 DCHECK(tracker->active()); 1189 1190 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1191 // Mark descendant trackers as dirty to handle changes in disable period. 1192 RecursiveMarkTrackerAsDirty(tracker_id, batch); 1193 PutTrackerToBatch(*tracker, batch); 1194} 1195 1196void MetadataDatabase::CreateTrackerForParentAndFileID( 1197 const FileTracker& parent_tracker, 1198 const std::string& file_id, 1199 leveldb::WriteBatch* batch) { 1200 int64 tracker_id = GetNextTrackerID(batch); 1201 scoped_ptr<FileTracker> tracker(new FileTracker); 1202 tracker->set_tracker_id(tracker_id); 1203 tracker->set_parent_tracker_id(parent_tracker.tracker_id()); 1204 tracker->set_file_id(file_id); 1205 tracker->set_app_id(parent_tracker.app_id()); 1206 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1207 tracker->set_dirty(true); 1208 tracker->set_active(false); 1209 tracker->set_needs_folder_listing(false); 1210 PutTrackerToBatch(*tracker, batch); 1211 1212 trackers_by_file_id_[file_id].Insert(tracker.get()); 1213 // Note: |trackers_by_parent_and_title_| does not map from 1214 // FileMetadata::details but from FileTracker::synced_details, which is filled 1215 // on tracker updated phase. Use empty string as the title since 1216 // FileTracker::synced_details is empty here. 1217 trackers_by_parent_and_title_[parent_tracker.tracker_id()][std::string()] 1218 .Insert(tracker.get()); 1219 dirty_trackers_.insert(tracker.get()); 1220 DCHECK(!ContainsKey(tracker_by_id_, tracker_id)); 1221 tracker_by_id_[tracker_id] = tracker.release(); 1222} 1223 1224void MetadataDatabase::RemoveTracker(int64 tracker_id, 1225 leveldb::WriteBatch* batch) { 1226 RemoveTrackerInternal(tracker_id, batch, false); 1227} 1228 1229void MetadataDatabase::RemoveTrackerIgnoringSiblings( 1230 int64 tracker_id, 1231 leveldb::WriteBatch* batch) { 1232 RemoveTrackerInternal(tracker_id, batch, true); 1233} 1234 1235void MetadataDatabase::RemoveTrackerInternal( 1236 int64 tracker_id, 1237 leveldb::WriteBatch* batch, 1238 bool ignoring_siblings) { 1239 scoped_ptr<FileTracker> tracker( 1240 FindAndEraseItem(&tracker_by_id_, tracker_id)); 1241 if (!tracker) 1242 return; 1243 1244 EraseTrackerFromFileIDIndex(tracker.get(), batch); 1245 if (IsAppRoot(*tracker)) 1246 app_root_by_app_id_.erase(tracker->app_id()); 1247 EraseTrackerFromPathIndex(tracker.get()); 1248 1249 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1250 if (!ignoring_siblings) { 1251 MarkTrackersDirtyByPath(tracker->parent_tracker_id(), 1252 GetTrackerTitle(*tracker), 1253 batch); 1254 } 1255 PutTrackerDeletionToBatch(tracker_id, batch); 1256} 1257 1258void MetadataDatabase::MaybeAddTrackersForNewFile( 1259 const FileMetadata& file, 1260 leveldb::WriteBatch* batch) { 1261 std::set<int64> known_parents; 1262 TrackersByFileID::iterator found = trackers_by_file_id_.find(file.file_id()); 1263 if (found != trackers_by_file_id_.end()) { 1264 for (TrackerSet::const_iterator itr = found->second.begin(); 1265 itr != found->second.end(); ++itr) { 1266 int64 parent_tracker_id = (*itr)->parent_tracker_id(); 1267 if (parent_tracker_id) 1268 known_parents.insert(parent_tracker_id); 1269 } 1270 } 1271 1272 for (int i = 0; i < file.details().parent_folder_ids_size(); ++i) { 1273 std::string parent_folder_id = file.details().parent_folder_ids(i); 1274 TrackersByFileID::iterator found = 1275 trackers_by_file_id_.find(parent_folder_id); 1276 if (found == trackers_by_file_id_.end()) 1277 continue; 1278 1279 for (TrackerSet::const_iterator itr = found->second.begin(); 1280 itr != found->second.end(); ++itr) { 1281 FileTracker* parent_tracker = *itr; 1282 int64 parent_tracker_id = parent_tracker->tracker_id(); 1283 if (!parent_tracker->active()) 1284 continue; 1285 1286 if (ContainsKey(known_parents, parent_tracker_id)) 1287 continue; 1288 1289 CreateTrackerForParentAndFileID(*parent_tracker, file.file_id(), batch); 1290 } 1291 } 1292} 1293 1294void MetadataDatabase::RemoveAllDescendantTrackers(int64 root_tracker_id, 1295 leveldb::WriteBatch* batch) { 1296 std::vector<int64> pending_trackers; 1297 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1298 root_tracker_id, 1299 std::back_inserter(pending_trackers)); 1300 1301 while (!pending_trackers.empty()) { 1302 int64 tracker_id = pending_trackers.back(); 1303 pending_trackers.pop_back(); 1304 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1305 tracker_id, 1306 std::back_inserter(pending_trackers)); 1307 RemoveTrackerIgnoringSiblings(tracker_id, batch); 1308 } 1309} 1310 1311void MetadataDatabase::EraseTrackerFromFileIDIndex(FileTracker* tracker, 1312 leveldb::WriteBatch* batch) { 1313 TrackersByFileID::iterator found = 1314 trackers_by_file_id_.find(tracker->file_id()); 1315 if (found == trackers_by_file_id_.end()) 1316 return; 1317 1318 TrackerSet* trackers = &found->second; 1319 trackers->Erase(tracker); 1320 if (!trackers->tracker_set().empty()) 1321 return; 1322 trackers_by_file_id_.erase(found); 1323 EraseFileFromDatabase(tracker->file_id(), batch); 1324} 1325 1326void MetadataDatabase::EraseFileFromDatabase(const std::string& file_id, 1327 leveldb::WriteBatch* batch) { 1328 scoped_ptr<FileMetadata> file(FindAndEraseItem(&file_by_id_, file_id)); 1329 if (file) 1330 PutFileDeletionToBatch(file_id, batch); 1331} 1332 1333void MetadataDatabase::EraseTrackerFromPathIndex(FileTracker* tracker) { 1334 TrackersByParentAndTitle::iterator found = 1335 trackers_by_parent_and_title_.find(tracker->parent_tracker_id()); 1336 if (found == trackers_by_parent_and_title_.end()) 1337 return; 1338 1339 std::string title = GetTrackerTitle(*tracker); 1340 TrackersByTitle* trackers_by_title = &found->second; 1341 TrackersByTitle::iterator found_by_title = trackers_by_title->find(title); 1342 TrackerSet* conflicting_trackers = &found_by_title->second; 1343 conflicting_trackers->Erase(tracker); 1344 1345 if (conflicting_trackers->tracker_set().empty()) { 1346 trackers_by_title->erase(found_by_title); 1347 if (trackers_by_title->empty()) 1348 trackers_by_parent_and_title_.erase(found); 1349 } 1350} 1351 1352void MetadataDatabase::MarkTrackerSetDirty( 1353 TrackerSet* trackers, 1354 leveldb::WriteBatch* batch) { 1355 for (TrackerSet::iterator itr = trackers->begin(); 1356 itr != trackers->end(); ++itr) { 1357 FileTracker* tracker = *itr; 1358 if (tracker->dirty()) 1359 continue; 1360 tracker->set_dirty(true); 1361 PutTrackerToBatch(*tracker, batch); 1362 dirty_trackers_.insert(tracker); 1363 } 1364} 1365 1366void MetadataDatabase::MarkTrackersDirtyByFileID( 1367 const std::string& file_id, 1368 leveldb::WriteBatch* batch) { 1369 TrackersByFileID::iterator found = trackers_by_file_id_.find(file_id); 1370 if (found != trackers_by_file_id_.end()) 1371 MarkTrackerSetDirty(&found->second, batch); 1372} 1373 1374void MetadataDatabase::MarkTrackersDirtyByPath(int64 parent_tracker_id, 1375 const std::string& title, 1376 leveldb::WriteBatch* batch) { 1377 TrackersByParentAndTitle::iterator found = 1378 trackers_by_parent_and_title_.find(parent_tracker_id); 1379 if (found == trackers_by_parent_and_title_.end()) { 1380 NOTREACHED() << "parent: " << parent_tracker_id 1381 << ", title: " << title; 1382 return; 1383 } 1384 1385 TrackersByTitle::iterator itr = found->second.find(title); 1386 if (itr != found->second.end()) 1387 MarkTrackerSetDirty(&itr->second, batch); 1388} 1389 1390int64 MetadataDatabase::GetNextTrackerID(leveldb::WriteBatch* batch) { 1391 int64 tracker_id = service_metadata_->next_tracker_id(); 1392 service_metadata_->set_next_tracker_id(tracker_id + 1); 1393 PutServiceMetadataToBatch(*service_metadata_, batch); 1394 DCHECK_GT(tracker_id, 0); 1395 return tracker_id; 1396} 1397 1398void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id, 1399 leveldb::WriteBatch* batch) { 1400 std::vector<int64> stack; 1401 stack.push_back(root_tracker_id); 1402 while (!stack.empty()) { 1403 int64 tracker_id = stack.back(); 1404 stack.pop_back(); 1405 PushChildTrackersToContainer( 1406 trackers_by_parent_and_title_, tracker_id, std::back_inserter(stack)); 1407 1408 FileTracker* tracker = tracker_by_id_[tracker_id]; 1409 if (!tracker->dirty()) { 1410 tracker->set_dirty(true); 1411 PutTrackerToBatch(*tracker, batch); 1412 dirty_trackers_.insert(tracker); 1413 } 1414 } 1415} 1416 1417bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) { 1418 DCHECK(!tracker.active()); 1419 DCHECK_NE(service_metadata_->sync_root_tracker_id(), tracker.tracker_id()); 1420 1421 if (HasActiveTrackerForFileID(tracker.file_id())) 1422 return false; 1423 1424 if (tracker.app_id().empty()) 1425 return false; 1426 if (!tracker.has_synced_details()) 1427 return false; 1428 DCHECK(tracker.parent_tracker_id()); 1429 1430 return !HasActiveTrackerForPath(tracker.parent_tracker_id(), 1431 tracker.synced_details().title()); 1432} 1433 1434bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { 1435 if (HasDisabledAppRoot(tracker)) 1436 return false; 1437 1438 DCHECK(tracker.dirty()); 1439 if (!tracker.has_synced_details()) 1440 return true; 1441 1442 FileByID::const_iterator found = file_by_id_.find(tracker.file_id()); 1443 if (found == file_by_id_.end()) 1444 return true; 1445 const FileMetadata* file = found->second; 1446 DCHECK(file); 1447 1448 if (tracker.active()) { 1449 if (tracker.needs_folder_listing()) 1450 return true; 1451 if (tracker.synced_details().md5() != file->details().md5()) 1452 return true; 1453 } 1454 1455 const FileDetails& local_details = tracker.synced_details(); 1456 const FileDetails& remote_details = file->details(); 1457 1458 if (local_details.title() != remote_details.title()) 1459 return true; 1460 if (local_details.deleted() != remote_details.deleted()) 1461 return true; 1462 1463 return false; 1464} 1465 1466bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const { 1467 TrackerByAppID::const_iterator found = 1468 app_root_by_app_id_.find(tracker.app_id()); 1469 if (found == app_root_by_app_id_.end()) 1470 return false; 1471 1472 const FileTracker* app_root_tracker = found->second; 1473 DCHECK(app_root_tracker); 1474 return app_root_tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 1475} 1476 1477bool MetadataDatabase::HasActiveTrackerForFileID( 1478 const std::string& file_id) const { 1479 TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id); 1480 return found != trackers_by_file_id_.end() && found->second.has_active(); 1481} 1482 1483bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id, 1484 const std::string& title) const { 1485 TrackersByParentAndTitle::const_iterator found_by_parent = 1486 trackers_by_parent_and_title_.find(parent_tracker_id); 1487 if (found_by_parent == trackers_by_parent_and_title_.end()) 1488 return false; 1489 1490 const TrackersByTitle& trackers_by_title = found_by_parent->second; 1491 TrackersByTitle::const_iterator found = trackers_by_title.find(title); 1492 return found != trackers_by_title.end() && found->second.has_active(); 1493} 1494 1495void MetadataDatabase::UpdateTrackerTitle(FileTracker* tracker, 1496 const std::string& new_title, 1497 leveldb::WriteBatch* batch) { 1498 int64 parent_id = tracker->parent_tracker_id(); 1499 std::string old_title = GetTrackerTitle(*tracker); 1500 DCHECK_NE(old_title, new_title); 1501 DCHECK(!new_title.empty()); 1502 1503 TrackersByTitle* trackers_by_title = 1504 &trackers_by_parent_and_title_[parent_id]; 1505 TrackerSet* old_siblings = &(*trackers_by_title)[old_title]; 1506 TrackerSet* new_siblings = &(*trackers_by_title)[new_title]; 1507 1508 old_siblings->Erase(tracker); 1509 if (old_siblings->empty()) 1510 trackers_by_title->erase(old_title); 1511 else 1512 MarkTrackerSetDirty(old_siblings, batch); 1513 1514 if (tracker->active() && new_siblings->has_active()) { 1515 // Inactivate existing active tracker. 1516 FileTracker* obstacle = new_siblings->active_tracker(); 1517 new_siblings->Inactivate(obstacle); 1518 DCHECK_EQ(TRACKER_KIND_REGULAR, obstacle->tracker_kind()); 1519 1520 TrackerSet* same_file_id_trackers_to_obstacle = 1521 &trackers_by_file_id_[obstacle->file_id()]; 1522 same_file_id_trackers_to_obstacle->Inactivate(obstacle); 1523 MarkTrackerSetDirty(same_file_id_trackers_to_obstacle, batch); 1524 1525 obstacle->set_active(false); 1526 PutTrackerToBatch(*obstacle, batch); 1527 1528 RemoveAllDescendantTrackers(obstacle->tracker_id(), batch); 1529 } 1530 1531 tracker->mutable_synced_details()->set_title(new_title); 1532 new_siblings->Insert(tracker); 1533 PutTrackerToBatch(*tracker, batch); 1534} 1535 1536void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 1537 const SyncStatusCallback& callback) { 1538 base::PostTaskAndReplyWithResult( 1539 task_runner_.get(), 1540 FROM_HERE, 1541 base::Bind(&leveldb::DB::Write, 1542 base::Unretained(db_.get()), 1543 leveldb::WriteOptions(), 1544 base::Owned(batch.release())), 1545 base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback)); 1546} 1547 1548} // namespace drive_backend 1549} // namespace sync_file_system 1550