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/file_util.h" 13 #include "base/files/file_path.h" 14 #include "base/location.h" 15 #include "base/memory/scoped_vector.h" 16 #include "base/message_loop/message_loop_proxy.h" 17 #include "base/sequenced_task_runner.h" 18 #include "base/stl_util.h" 19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/task_runner_util.h" 23 #include "base/threading/thread_restrictions.h" 24 #include "chrome/browser/drive/drive_api_util.h" 25 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 26 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" 27 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 28 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h" 29 #include "chrome/browser/sync_file_system/logger.h" 30 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 31 #include "google_apis/drive/drive_api_parser.h" 32 #include "google_apis/drive/drive_entry_kinds.h" 33 #include "third_party/leveldatabase/src/include/leveldb/db.h" 34 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 35 #include "webkit/common/fileapi/file_system_util.h" 36 37 namespace sync_file_system { 38 namespace drive_backend { 39 40 struct DatabaseContents { 41 scoped_ptr<ServiceMetadata> service_metadata; 42 ScopedVector<FileMetadata> file_metadata; 43 ScopedVector<FileTracker> file_trackers; 44 }; 45 46 namespace { 47 48 typedef MetadataDatabase::FileByID FileByID; 49 typedef MetadataDatabase::TrackerByID TrackerByID; 50 typedef MetadataDatabase::TrackersByParentAndTitle TrackersByParentAndTitle; 51 typedef MetadataDatabase::TrackersByTitle TrackersByTitle; 52 53 bool IsAppRoot(const FileTracker& tracker) { 54 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT || 55 tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 56 } 57 58 std::string RemovePrefix(const std::string& str, const std::string& prefix) { 59 if (StartsWithASCII(str, prefix, true)) 60 return str.substr(prefix.size()); 61 return str; 62 } 63 64 base::FilePath ReverseConcatPathComponents( 65 const std::vector<base::FilePath>& components) { 66 if (components.empty()) 67 return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators(); 68 69 size_t total_size = 0; 70 typedef std::vector<base::FilePath> PathComponents; 71 for (PathComponents::const_iterator itr = components.begin(); 72 itr != components.end(); ++itr) 73 total_size += itr->value().size() + 1; 74 75 base::FilePath::StringType result; 76 result.reserve(total_size); 77 for (PathComponents::const_reverse_iterator itr = components.rbegin(); 78 itr != components.rend(); ++itr) { 79 result.append(1, base::FilePath::kSeparators[0]); 80 result.append(itr->value()); 81 } 82 83 return base::FilePath(result).NormalizePathSeparators(); 84 } 85 86 void CreateInitialSyncRootTracker( 87 int64 tracker_id, 88 const google_apis::FileResource& file_resource, 89 scoped_ptr<FileMetadata>* file_out, 90 scoped_ptr<FileTracker>* tracker_out) { 91 FileDetails details; 92 PopulateFileDetailsByFileResource(file_resource, &details); 93 94 scoped_ptr<FileMetadata> file(new FileMetadata); 95 file->set_file_id(file_resource.file_id()); 96 *file->mutable_details() = details; 97 98 scoped_ptr<FileTracker> tracker(new FileTracker); 99 tracker->set_tracker_id(tracker_id); 100 tracker->set_file_id(file_resource.file_id()); 101 tracker->set_parent_tracker_id(0); 102 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 103 tracker->set_dirty(false); 104 tracker->set_active(true); 105 tracker->set_needs_folder_listing(false); 106 *tracker->mutable_synced_details() = details; 107 108 *file_out = file.Pass(); 109 *tracker_out = tracker.Pass(); 110 } 111 112 void CreateInitialAppRootTracker( 113 int64 tracker_id, 114 const FileTracker& parent_tracker, 115 const google_apis::FileResource& file_resource, 116 scoped_ptr<FileMetadata>* file_out, 117 scoped_ptr<FileTracker>* tracker_out) { 118 FileDetails details; 119 PopulateFileDetailsByFileResource(file_resource, &details); 120 121 scoped_ptr<FileMetadata> file(new FileMetadata); 122 file->set_file_id(file_resource.file_id()); 123 *file->mutable_details() = details; 124 125 scoped_ptr<FileTracker> tracker(new FileTracker); 126 tracker->set_tracker_id(tracker_id); 127 tracker->set_parent_tracker_id(parent_tracker.tracker_id()); 128 tracker->set_file_id(file_resource.file_id()); 129 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 130 tracker->set_dirty(false); 131 tracker->set_active(false); 132 tracker->set_needs_folder_listing(false); 133 *tracker->mutable_synced_details() = details; 134 135 *file_out = file.Pass(); 136 *tracker_out = tracker.Pass(); 137 } 138 139 void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback, 140 const leveldb::Status& status) { 141 callback.Run(LevelDBStatusToSyncStatusCode(status)); 142 } 143 144 void PutFileDeletionToBatch(const std::string& file_id, 145 leveldb::WriteBatch* batch) { 146 batch->Delete(kFileMetadataKeyPrefix + file_id); 147 } 148 149 void PutTrackerDeletionToBatch(int64 tracker_id, leveldb::WriteBatch* batch) { 150 batch->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id)); 151 } 152 153 template <typename OutputIterator> 154 OutputIterator PushChildTrackersToContainer( 155 const TrackersByParentAndTitle& trackers_by_parent, 156 int64 parent_tracker_id, 157 OutputIterator target_itr) { 158 TrackersByParentAndTitle::const_iterator found = 159 trackers_by_parent.find(parent_tracker_id); 160 if (found == trackers_by_parent.end()) 161 return target_itr; 162 163 for (TrackersByTitle::const_iterator title_itr = found->second.begin(); 164 title_itr != found->second.end(); ++title_itr) { 165 const TrackerSet& trackers = title_itr->second; 166 for (TrackerSet::const_iterator tracker_itr = trackers.begin(); 167 tracker_itr != trackers.end(); ++tracker_itr) { 168 *target_itr = (*tracker_itr)->tracker_id(); 169 ++target_itr; 170 } 171 } 172 return target_itr; 173 } 174 175 std::string GetTrackerTitle(const FileTracker& tracker) { 176 if (tracker.has_synced_details()) 177 return tracker.synced_details().title(); 178 return std::string(); 179 } 180 181 // Returns true if |db| has no content. 182 bool IsDatabaseEmpty(leveldb::DB* db) { 183 DCHECK(db); 184 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 185 itr->SeekToFirst(); 186 return !itr->Valid(); 187 } 188 189 SyncStatusCode OpenDatabase(const base::FilePath& path, 190 scoped_ptr<leveldb::DB>* db_out, 191 bool* created) { 192 base::ThreadRestrictions::AssertIOAllowed(); 193 DCHECK(db_out); 194 DCHECK(created); 195 196 leveldb::Options options; 197 options.max_open_files = 0; // Use minimum. 198 options.create_if_missing = true; 199 leveldb::DB* db = NULL; 200 leveldb::Status db_status = 201 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db); 202 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status); 203 if (status != SYNC_STATUS_OK) { 204 delete db; 205 return status; 206 } 207 208 *created = IsDatabaseEmpty(db); 209 db_out->reset(db); 210 return status; 211 } 212 213 SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) { 214 base::ThreadRestrictions::AssertIOAllowed(); 215 DCHECK(db); 216 std::string value; 217 leveldb::Status status = 218 db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); 219 int64 version = 0; 220 if (status.ok()) { 221 if (!base::StringToInt64(value, &version)) 222 return SYNC_DATABASE_ERROR_FAILED; 223 } else { 224 if (!status.IsNotFound()) 225 return SYNC_DATABASE_ERROR_FAILED; 226 } 227 228 switch (version) { 229 case 0: 230 drive_backend::MigrateDatabaseFromV0ToV1(db); 231 // fall-through 232 case 1: 233 drive_backend::MigrateDatabaseFromV1ToV2(db); 234 // fall-through 235 case 2: 236 // TODO(tzik): Migrate database from version 2 to 3. 237 // * Add sync-root folder as active, dirty and needs_folder_listing 238 // folder. 239 // * Add app-root folders for each origins. Each app-root folder for 240 // an enabled origin should be a active, dirty and 241 // needs_folder_listing folder. And Each app-root folder for a 242 // disabled origin should be an inactive, dirty and 243 // non-needs_folder_listing folder. 244 // * Add a file metadata for each file in previous version. 245 NOTIMPLEMENTED(); 246 return SYNC_DATABASE_ERROR_FAILED; 247 // fall-through 248 case 3: 249 DCHECK_EQ(3, kCurrentDatabaseVersion); 250 return SYNC_STATUS_OK; 251 default: 252 return SYNC_DATABASE_ERROR_FAILED; 253 } 254 } 255 256 SyncStatusCode WriteVersionInfo(leveldb::DB* db) { 257 base::ThreadRestrictions::AssertIOAllowed(); 258 DCHECK(db); 259 return LevelDBStatusToSyncStatusCode( 260 db->Put(leveldb::WriteOptions(), 261 kDatabaseVersionKey, 262 base::Int64ToString(kCurrentDatabaseVersion))); 263 } 264 265 SyncStatusCode ReadDatabaseContents(leveldb::DB* db, 266 DatabaseContents* contents) { 267 base::ThreadRestrictions::AssertIOAllowed(); 268 DCHECK(db); 269 DCHECK(contents); 270 271 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 272 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) { 273 std::string key = itr->key().ToString(); 274 std::string value = itr->value().ToString(); 275 if (key == kServiceMetadataKey) { 276 scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata); 277 if (!service_metadata->ParseFromString(value)) { 278 util::Log(logging::LOG_WARNING, FROM_HERE, 279 "Failed to parse SyncServiceMetadata"); 280 continue; 281 } 282 283 contents->service_metadata = service_metadata.Pass(); 284 continue; 285 } 286 287 if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) { 288 std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix); 289 290 scoped_ptr<FileMetadata> file(new FileMetadata); 291 if (!file->ParseFromString(itr->value().ToString())) { 292 util::Log(logging::LOG_WARNING, FROM_HERE, 293 "Failed to parse a FileMetadata"); 294 continue; 295 } 296 297 contents->file_metadata.push_back(file.release()); 298 continue; 299 } 300 301 if (StartsWithASCII(key, kFileTrackerKeyPrefix, true)) { 302 int64 tracker_id = 0; 303 if (!base::StringToInt64(RemovePrefix(key, kFileTrackerKeyPrefix), 304 &tracker_id)) { 305 util::Log(logging::LOG_WARNING, FROM_HERE, 306 "Failed to parse TrackerID"); 307 continue; 308 } 309 310 scoped_ptr<FileTracker> tracker(new FileTracker); 311 if (!tracker->ParseFromString(itr->value().ToString())) { 312 util::Log(logging::LOG_WARNING, FROM_HERE, 313 "Failed to parse a Tracker"); 314 continue; 315 } 316 contents->file_trackers.push_back(tracker.release()); 317 continue; 318 } 319 } 320 321 return SYNC_STATUS_OK; 322 } 323 324 SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents, 325 leveldb::WriteBatch* batch) { 326 if (!contents->service_metadata) { 327 contents->service_metadata.reset(new ServiceMetadata); 328 contents->service_metadata->set_next_tracker_id(1); 329 330 std::string value; 331 contents->service_metadata->SerializeToString(&value); 332 batch->Put(kServiceMetadataKey, value); 333 } 334 return SYNC_STATUS_OK; 335 } 336 337 SyncStatusCode RemoveUnreachableItems(DatabaseContents* contents, 338 leveldb::WriteBatch* batch) { 339 TrackerByID unvisited_trackers; 340 typedef std::map<int64, std::set<FileTracker*> > TrackersByParent; 341 TrackersByParent trackers_by_parent; 342 343 for (ScopedVector<FileTracker>::iterator itr = 344 contents->file_trackers.begin(); 345 itr != contents->file_trackers.end(); 346 ++itr) { 347 FileTracker* tracker = *itr; 348 DCHECK(!ContainsKey(unvisited_trackers, tracker->tracker_id())); 349 unvisited_trackers[tracker->tracker_id()] = tracker; 350 if (tracker->parent_tracker_id()) 351 trackers_by_parent[tracker->parent_tracker_id()].insert(tracker); 352 } 353 354 // Traverse synced tracker tree. Take only active items, app-root and their 355 // children. Drop unreachable items. 356 ScopedVector<FileTracker> reachable_trackers; 357 std::stack<int64> pending; 358 if (contents->service_metadata->sync_root_tracker_id()) 359 pending.push(contents->service_metadata->sync_root_tracker_id()); 360 361 while (!pending.empty()) { 362 int64 tracker_id = pending.top(); 363 pending.pop(); 364 365 { 366 TrackerByID::iterator found = unvisited_trackers.find(tracker_id); 367 if (found == unvisited_trackers.end()) 368 continue; 369 370 FileTracker* tracker = found->second; 371 unvisited_trackers.erase(found); 372 reachable_trackers.push_back(tracker); 373 374 if (!tracker->active()) 375 continue; 376 } 377 378 TrackersByParent::iterator found = trackers_by_parent.find(tracker_id); 379 if (found == trackers_by_parent.end()) 380 continue; 381 382 for (std::set<FileTracker*>::const_iterator itr = 383 found->second.begin(); 384 itr != found->second.end(); 385 ++itr) 386 pending.push((*itr)->tracker_id()); 387 } 388 389 // Delete all unreachable trackers. 390 for (TrackerByID::iterator itr = unvisited_trackers.begin(); 391 itr != unvisited_trackers.end(); ++itr) { 392 FileTracker* tracker = itr->second; 393 PutTrackerDeletionToBatch(tracker->tracker_id(), batch); 394 delete tracker; 395 } 396 unvisited_trackers.clear(); 397 398 // |reachable_trackers| contains all files/folders reachable from sync-root 399 // folder via active folders and app-root folders. 400 // Update the tracker set in database contents with the reachable tracker set. 401 contents->file_trackers.weak_clear(); 402 contents->file_trackers.swap(reachable_trackers); 403 404 // Do the similar traverse for FileMetadata and remove FileMetadata that don't 405 // have reachable trackers. 406 FileByID unreferred_files; 407 for (ScopedVector<FileMetadata>::const_iterator itr = 408 contents->file_metadata.begin(); 409 itr != contents->file_metadata.end(); 410 ++itr) { 411 unreferred_files.insert(std::make_pair((*itr)->file_id(), *itr)); 412 } 413 414 ScopedVector<FileMetadata> referred_files; 415 for (ScopedVector<FileTracker>::const_iterator itr = 416 contents->file_trackers.begin(); 417 itr != contents->file_trackers.end(); 418 ++itr) { 419 FileByID::iterator found = unreferred_files.find((*itr)->file_id()); 420 if (found != unreferred_files.end()) { 421 referred_files.push_back(found->second); 422 unreferred_files.erase(found); 423 } 424 } 425 426 for (FileByID::iterator itr = unreferred_files.begin(); 427 itr != unreferred_files.end(); ++itr) { 428 FileMetadata* file = itr->second; 429 PutFileDeletionToBatch(file->file_id(), batch); 430 delete file; 431 } 432 unreferred_files.clear(); 433 434 contents->file_metadata.weak_clear(); 435 contents->file_metadata.swap(referred_files); 436 437 return SYNC_STATUS_OK; 438 } 439 440 template <typename Container, typename Key, typename Value> 441 bool FindItem(const Container& container, const Key& key, Value* value) { 442 typename Container::const_iterator found = container.find(key); 443 if (found == container.end()) 444 return false; 445 if (value) 446 *value = *found->second; 447 return true; 448 } 449 450 template <typename Container, typename Key> 451 typename Container::mapped_type FindAndEraseItem(Container* container, 452 const Key& key) { 453 typedef typename Container::mapped_type Value; 454 typename Container::iterator found = container->find(key); 455 if (found == container->end()) 456 return Value(); 457 458 Value result = found->second; 459 container->erase(found); 460 return result; 461 } 462 463 void RunSoon(const tracked_objects::Location& from_here, 464 const base::Closure& closure) { 465 base::MessageLoopProxy::current()->PostTask(from_here, closure); 466 } 467 468 bool HasInvalidTitle(const std::string& title) { 469 return title.find('/') != std::string::npos || 470 title.find('\\') != std::string::npos; 471 } 472 473 } // namespace 474 475 bool MetadataDatabase::DirtyTrackerComparator::operator()( 476 const FileTracker* left, 477 const FileTracker* right) const { 478 return left->tracker_id() < right->tracker_id(); 479 } 480 481 // static 482 void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner, 483 const base::FilePath& database_path, 484 const CreateCallback& callback) { 485 task_runner->PostTask(FROM_HERE, base::Bind( 486 &CreateOnTaskRunner, 487 base::MessageLoopProxy::current(), 488 make_scoped_refptr(task_runner), 489 database_path, callback)); 490 } 491 492 MetadataDatabase::~MetadataDatabase() { 493 task_runner_->DeleteSoon(FROM_HERE, db_.release()); 494 STLDeleteContainerPairSecondPointers( 495 file_by_id_.begin(), file_by_id_.end()); 496 STLDeleteContainerPairSecondPointers( 497 tracker_by_id_.begin(), tracker_by_id_.end()); 498 } 499 500 // static 501 void MetadataDatabase::ClearDatabase( 502 scoped_ptr<MetadataDatabase> metadata_database) { 503 DCHECK(metadata_database); 504 scoped_refptr<base::SequencedTaskRunner> task_runner = 505 metadata_database->task_runner_; 506 base::FilePath database_path = metadata_database->database_path_; 507 DCHECK(!database_path.empty()); 508 metadata_database.reset(); 509 510 task_runner->PostTask( 511 FROM_HERE, 512 base::Bind(base::IgnoreResult(base::DeleteFile), 513 database_path, true /* recursive */)); 514 } 515 516 int64 MetadataDatabase::GetLargestFetchedChangeID() const { 517 return service_metadata_->largest_change_id(); 518 } 519 520 int64 MetadataDatabase::GetSyncRootTrackerID() const { 521 return service_metadata_->sync_root_tracker_id(); 522 } 523 524 int64 MetadataDatabase::GetLargestKnownChangeID() const { 525 DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_); 526 return largest_known_change_id_; 527 } 528 529 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) { 530 if (largest_known_change_id_ < change_id) 531 largest_known_change_id_ = change_id; 532 } 533 534 bool MetadataDatabase::HasSyncRoot() const { 535 return service_metadata_->has_sync_root_tracker_id() && 536 !!service_metadata_->sync_root_tracker_id(); 537 } 538 539 void MetadataDatabase::PopulateInitialData( 540 int64 largest_change_id, 541 const google_apis::FileResource& sync_root_folder, 542 const ScopedVector<google_apis::FileResource>& app_root_folders, 543 const SyncStatusCallback& callback) { 544 DCHECK(tracker_by_id_.empty()); 545 DCHECK(file_by_id_.empty()); 546 547 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 548 service_metadata_->set_largest_change_id(largest_change_id); 549 UpdateLargestKnownChangeID(largest_change_id); 550 551 FileTracker* sync_root_tracker = NULL; 552 int64 sync_root_tracker_id = 0; 553 { 554 scoped_ptr<FileMetadata> folder; 555 scoped_ptr<FileTracker> tracker; 556 CreateInitialSyncRootTracker(GetNextTrackerID(batch.get()), 557 sync_root_folder, 558 &folder, 559 &tracker); 560 std::string sync_root_folder_id = folder->file_id(); 561 sync_root_tracker = tracker.get(); 562 sync_root_tracker_id = tracker->tracker_id(); 563 564 PutFileToBatch(*folder, batch.get()); 565 PutTrackerToBatch(*tracker, batch.get()); 566 567 service_metadata_->set_sync_root_tracker_id(tracker->tracker_id()); 568 PutServiceMetadataToBatch(*service_metadata_, batch.get()); 569 570 trackers_by_file_id_[folder->file_id()].Insert(tracker.get()); 571 572 file_by_id_[sync_root_folder_id] = folder.release(); 573 tracker_by_id_[sync_root_tracker_id] = tracker.release(); 574 } 575 576 for (ScopedVector<google_apis::FileResource>::const_iterator itr = 577 app_root_folders.begin(); 578 itr != app_root_folders.end(); 579 ++itr) { 580 const google_apis::FileResource& folder_resource = **itr; 581 scoped_ptr<FileMetadata> folder; 582 scoped_ptr<FileTracker> tracker; 583 CreateInitialAppRootTracker(GetNextTrackerID(batch.get()), 584 *sync_root_tracker, 585 folder_resource, 586 &folder, 587 &tracker); 588 std::string title = folder->details().title(); 589 std::string folder_id = folder->file_id(); 590 int64 tracker_id = tracker->tracker_id(); 591 592 PutFileToBatch(*folder, batch.get()); 593 PutTrackerToBatch(*tracker, batch.get()); 594 595 trackers_by_file_id_[folder_id].Insert(tracker.get()); 596 trackers_by_parent_and_title_[sync_root_tracker_id][title] 597 .Insert(tracker.get()); 598 599 file_by_id_[folder_id] = folder.release(); 600 tracker_by_id_[tracker_id] = tracker.release(); 601 } 602 603 WriteToDatabase(batch.Pass(), callback); 604 } 605 606 bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const { 607 FileTracker tracker; 608 if (!FindAppRootTracker(app_id, &tracker)) 609 return false; 610 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT; 611 } 612 613 void MetadataDatabase::RegisterApp(const std::string& app_id, 614 const std::string& folder_id, 615 const SyncStatusCallback& callback) { 616 if (FindAppRootTracker(app_id, NULL)) { 617 // The app-root is already registered. 618 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 619 return; 620 } 621 622 TrackerSet trackers; 623 if (!FindTrackersByFileID(folder_id, &trackers) || trackers.has_active()) { 624 // The folder is tracked by another tracker. 625 util::Log(logging::LOG_WARNING, FROM_HERE, 626 "Failed to register App for %s", app_id.c_str()); 627 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_HAS_CONFLICT)); 628 return; 629 } 630 631 int64 sync_root_tracker_id = service_metadata_->sync_root_tracker_id(); 632 if (!sync_root_tracker_id) { 633 util::Log(logging::LOG_WARNING, FROM_HERE, 634 "Sync-root needs to be set up before registering app-root"); 635 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 636 return; 637 } 638 639 // Make this tracker an app-root tracker. 640 FileTracker* app_root_tracker = NULL; 641 for (TrackerSet::iterator itr = trackers.begin(); 642 itr != trackers.end(); ++itr) { 643 FileTracker* tracker = *itr; 644 if (tracker->parent_tracker_id() == sync_root_tracker_id) 645 app_root_tracker = tracker; 646 } 647 648 if (!app_root_tracker) { 649 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 650 return; 651 } 652 653 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 654 RegisterTrackerAsAppRoot(app_id, app_root_tracker->tracker_id(), batch.get()); 655 WriteToDatabase(batch.Pass(), callback); 656 } 657 658 void MetadataDatabase::DisableApp(const std::string& app_id, 659 const SyncStatusCallback& callback) { 660 FileTracker tracker; 661 if (!FindAppRootTracker(app_id, &tracker)) { 662 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 663 return; 664 } 665 666 if (tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) { 667 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 668 return; 669 } 670 671 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 672 MakeAppRootDisabled(tracker.tracker_id(), batch.get()); 673 WriteToDatabase(batch.Pass(), callback); 674 } 675 676 void MetadataDatabase::EnableApp(const std::string& app_id, 677 const SyncStatusCallback& callback) { 678 FileTracker tracker; 679 if (!FindAppRootTracker(app_id, &tracker) || 680 tracker.tracker_kind() == TRACKER_KIND_REGULAR) { 681 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 682 return; 683 } 684 685 if (tracker.tracker_kind() == TRACKER_KIND_APP_ROOT) { 686 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 687 return; 688 } 689 690 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 691 MakeAppRootEnabled(tracker.tracker_id(), batch.get()); 692 WriteToDatabase(batch.Pass(), callback); 693 } 694 695 void MetadataDatabase::UnregisterApp(const std::string& app_id, 696 const SyncStatusCallback& callback) { 697 FileTracker tracker; 698 if (!FindAppRootTracker(app_id, &tracker) || 699 tracker.tracker_kind() == TRACKER_KIND_REGULAR) { 700 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 701 return; 702 } 703 704 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 705 UnregisterTrackerAsAppRoot(app_id, batch.get()); 706 WriteToDatabase(batch.Pass(), callback); 707 } 708 709 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id, 710 FileTracker* tracker) const { 711 return FindItem(app_root_by_app_id_, app_id, tracker); 712 } 713 714 bool MetadataDatabase::FindFileByFileID(const std::string& file_id, 715 FileMetadata* file) const { 716 return FindItem(file_by_id_, file_id, file); 717 } 718 719 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id, 720 TrackerSet* trackers) const { 721 TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id); 722 if (found == trackers_by_file_id_.end()) 723 return false; 724 if (trackers) 725 *trackers = found->second; 726 return true; 727 } 728 729 bool MetadataDatabase::FindTrackersByParentAndTitle( 730 int64 parent_tracker_id, 731 const std::string& title, 732 TrackerSet* trackers) const { 733 TrackersByParentAndTitle::const_iterator found_by_parent = 734 trackers_by_parent_and_title_.find(parent_tracker_id); 735 if (found_by_parent == trackers_by_parent_and_title_.end()) 736 return false; 737 738 TrackersByTitle::const_iterator found_by_title = 739 found_by_parent->second.find(title); 740 if (found_by_title == found_by_parent->second.end()) 741 return false; 742 743 if (trackers) 744 *trackers = found_by_title->second; 745 return true; 746 } 747 748 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id, 749 FileTracker* tracker) const { 750 return FindItem(tracker_by_id_, tracker_id, tracker); 751 } 752 753 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id, 754 base::FilePath* path) const { 755 FileTracker current; 756 if (!FindTrackerByTrackerID(tracker_id, ¤t) || !current.active()) 757 return false; 758 759 std::vector<base::FilePath> components; 760 while (!IsAppRoot(current)) { 761 std::string title = GetTrackerTitle(current); 762 if (title.empty()) 763 return false; 764 components.push_back(base::FilePath::FromUTF8Unsafe(title)); 765 if (!FindTrackerByTrackerID(current.parent_tracker_id(), ¤t) || 766 !current.active()) 767 return false; 768 } 769 770 if (path) 771 *path = ReverseConcatPathComponents(components); 772 773 return true; 774 } 775 776 base::FilePath MetadataDatabase::BuildDisplayPathForTracker( 777 const FileTracker& tracker) const { 778 base::FilePath path; 779 if (tracker.active()) { 780 BuildPathForTracker(tracker.tracker_id(), &path); 781 return path; 782 } 783 BuildPathForTracker(tracker.parent_tracker_id(), &path); 784 if (tracker.has_synced_details()) { 785 path = path.Append( 786 base::FilePath::FromUTF8Unsafe(tracker.synced_details().title())); 787 } else { 788 path = path.Append(FILE_PATH_LITERAL("<unknown>")); 789 } 790 return path; 791 } 792 793 bool MetadataDatabase::FindNearestActiveAncestor( 794 const std::string& app_id, 795 const base::FilePath& full_path, 796 FileTracker* tracker, 797 base::FilePath* path) const { 798 DCHECK(tracker); 799 DCHECK(path); 800 801 if (full_path.IsAbsolute() || 802 !FindAppRootTracker(app_id, tracker) || 803 tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) { 804 return false; 805 } 806 807 std::vector<base::FilePath::StringType> components; 808 full_path.GetComponents(&components); 809 path->clear(); 810 811 for (size_t i = 0; i < components.size(); ++i) { 812 const std::string title = base::FilePath(components[i]).AsUTF8Unsafe(); 813 TrackerSet trackers; 814 if (!FindTrackersByParentAndTitle( 815 tracker->tracker_id(), title, &trackers) || 816 !trackers.has_active()) { 817 return true; 818 } 819 820 DCHECK(trackers.active_tracker()->has_synced_details()); 821 const FileDetails& details = trackers.active_tracker()->synced_details(); 822 if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) { 823 // This non-last component indicates file. Give up search. 824 return true; 825 } 826 827 *tracker = *trackers.active_tracker(); 828 *path = path->Append(components[i]); 829 } 830 831 return true; 832 } 833 834 void MetadataDatabase::UpdateByChangeList( 835 int64 largest_change_id, 836 ScopedVector<google_apis::ChangeResource> changes, 837 const SyncStatusCallback& callback) { 838 DCHECK_LE(service_metadata_->largest_change_id(), largest_change_id); 839 840 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 841 842 for (ScopedVector<google_apis::ChangeResource>::const_iterator itr = 843 changes.begin(); 844 itr != changes.end(); 845 ++itr) { 846 const google_apis::ChangeResource& change = **itr; 847 if (HasNewerFileMetadata(change.file_id(), change.change_id())) 848 continue; 849 850 scoped_ptr<FileMetadata> file( 851 CreateFileMetadataFromChangeResource(change)); 852 UpdateByFileMetadata(FROM_HERE, file.Pass(), batch.get()); 853 } 854 855 UpdateLargestKnownChangeID(largest_change_id); 856 service_metadata_->set_largest_change_id(largest_change_id); 857 PutServiceMetadataToBatch(*service_metadata_, batch.get()); 858 WriteToDatabase(batch.Pass(), callback); 859 } 860 861 void MetadataDatabase::UpdateByFileResource( 862 const google_apis::FileResource& resource, 863 const SyncStatusCallback& callback) { 864 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 865 866 scoped_ptr<FileMetadata> file( 867 CreateFileMetadataFromFileResource( 868 GetLargestKnownChangeID(), resource)); 869 UpdateByFileMetadata(FROM_HERE, file.Pass(), batch.get()); 870 WriteToDatabase(batch.Pass(), callback); 871 } 872 873 void MetadataDatabase::UpdateByFileResourceList( 874 ScopedVector<google_apis::FileResource> resources, 875 const SyncStatusCallback& callback) { 876 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 877 878 for (size_t i = 0; i < resources.size(); ++i) { 879 scoped_ptr<FileMetadata> file( 880 CreateFileMetadataFromFileResource( 881 GetLargestKnownChangeID(), *resources[i])); 882 UpdateByFileMetadata(FROM_HERE, file.Pass(), batch.get()); 883 } 884 WriteToDatabase(batch.Pass(), callback); 885 } 886 887 void MetadataDatabase::UpdateByDeletedRemoteFile( 888 const std::string& file_id, 889 const SyncStatusCallback& callback) { 890 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 891 scoped_ptr<FileMetadata> file( 892 CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id)); 893 UpdateByFileMetadata(FROM_HERE, file.Pass(), batch.get()); 894 WriteToDatabase(batch.Pass(), callback); 895 } 896 897 void MetadataDatabase::ReplaceActiveTrackerWithNewResource( 898 int64 parent_tracker_id, 899 const google_apis::FileResource& resource, 900 const SyncStatusCallback& callback) { 901 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 902 903 scoped_ptr<FileMetadata> file( 904 CreateFileMetadataFromFileResource( 905 GetLargestKnownChangeID(), resource)); 906 std::string file_id = file->file_id(); 907 DCHECK(!ContainsKey(file_by_id_, file_id)); 908 DCHECK(!file->details().missing()); 909 910 // TODO(tzik): Consolidate with UpdateByChangeList. 911 MaybeAddTrackersForNewFile(*file, batch.get()); 912 913 const FileDetails& new_file_details = file->details(); 914 PutFileToBatch(*file, batch.get()); 915 file_by_id_[file_id] = file.release(); 916 917 TrackerSet new_trackers; 918 if (!FindTrackersByFileID(file_id, &new_trackers)) { 919 NOTREACHED(); 920 WriteToDatabase(batch.Pass(), callback); 921 return; 922 } 923 DCHECK_EQ(1u, new_trackers.size()); 924 925 FileTracker* new_tracker = *new_trackers.begin(); 926 DCHECK(!new_tracker->active()); 927 DCHECK(new_tracker->has_synced_details()); 928 DCHECK_EQ(parent_tracker_id, new_tracker->parent_tracker_id()); 929 930 std::string title = new_file_details.title(); 931 TrackerSet trackers; 932 if (FindTrackersByParentAndTitle(parent_tracker_id, title, &trackers) && 933 trackers.has_active()) 934 MakeTrackerInactive(trackers.active_tracker()->tracker_id(), batch.get()); 935 936 MakeTrackerActive(new_tracker->tracker_id(), batch.get()); 937 938 if (new_tracker->synced_details().title() != title) { 939 trackers_by_parent_and_title_[parent_tracker_id] 940 [GetTrackerTitle(*new_tracker)].Erase(new_tracker); 941 trackers_by_parent_and_title_[parent_tracker_id][title].Insert( 942 new_tracker); 943 } 944 *new_tracker->mutable_synced_details() = new_file_details; 945 946 new_tracker->set_dirty(false); 947 dirty_trackers_.erase(new_tracker); 948 low_priority_dirty_trackers_.erase(new_tracker); 949 PutTrackerToBatch(*new_tracker, batch.get()); 950 951 WriteToDatabase(batch.Pass(), callback); 952 } 953 954 void MetadataDatabase::PopulateFolderByChildList( 955 const std::string& folder_id, 956 const FileIDList& child_file_ids, 957 const SyncStatusCallback& callback) { 958 TrackerSet trackers; 959 if (!FindTrackersByFileID(folder_id, &trackers) || 960 !trackers.has_active()) { 961 // It's OK that there is no folder to populate its children. 962 // Inactive folders should ignore their contents updates. 963 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 964 return; 965 } 966 967 FileTracker* folder_tracker = 968 tracker_by_id_[trackers.active_tracker()->tracker_id()]; 969 DCHECK(folder_tracker); 970 std::set<std::string> children(child_file_ids.begin(), child_file_ids.end()); 971 972 std::vector<int64> known_children; 973 PushChildTrackersToContainer(trackers_by_parent_and_title_, 974 folder_tracker->tracker_id(), 975 std::back_inserter(known_children)); 976 for (std::vector<int64>::iterator itr = known_children.begin(); 977 itr != known_children.end(); ++itr) 978 children.erase(tracker_by_id_[*itr]->file_id()); 979 980 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 981 for (FileIDList::const_iterator itr = child_file_ids.begin(); 982 itr != child_file_ids.end(); ++itr) 983 CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get()); 984 folder_tracker->set_needs_folder_listing(false); 985 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker)) { 986 folder_tracker->set_dirty(false); 987 dirty_trackers_.erase(folder_tracker); 988 low_priority_dirty_trackers_.erase(folder_tracker); 989 } 990 PutTrackerToBatch(*folder_tracker, batch.get()); 991 992 WriteToDatabase(batch.Pass(), callback); 993 } 994 995 void MetadataDatabase::UpdateTracker(int64 tracker_id, 996 const FileDetails& updated_details, 997 const SyncStatusCallback& callback) { 998 TrackerByID::iterator found = tracker_by_id_.find(tracker_id); 999 if (found == tracker_by_id_.end()) { 1000 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 1001 return; 1002 } 1003 1004 FileTracker* tracker = found->second; 1005 DCHECK(tracker); 1006 1007 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 1008 1009 if (updated_details.missing()) { 1010 FileByID::iterator found = file_by_id_.find(tracker->file_id()); 1011 if (found == file_by_id_.end() || found->second->details().missing()) { 1012 // Both the tracker and metadata have the missing flag, now it's safe to 1013 // delete the |tracker|. 1014 RemoveTracker(tracker->tracker_id(), batch.get()); 1015 WriteToDatabase(batch.Pass(), callback); 1016 return; 1017 } 1018 } 1019 1020 // Sync-root deletion should be handled separately by SyncEngine. 1021 DCHECK(tracker_id != GetSyncRootTrackerID() || 1022 (tracker->has_synced_details() && 1023 tracker->synced_details().title() == updated_details.title() && 1024 !updated_details.missing())); 1025 1026 if (tracker_id != GetSyncRootTrackerID()) { 1027 // Check if the tracker's parent is still in |parent_tracker_ids|. 1028 // If not, there should exist another tracker for the new parent, so delete 1029 // old tracker. 1030 DCHECK(ContainsKey(tracker_by_id_, tracker->parent_tracker_id())); 1031 FileTracker* parent_tracker = tracker_by_id_[tracker->parent_tracker_id()]; 1032 if (!HasFileAsParent(updated_details, parent_tracker->file_id())) { 1033 RemoveTracker(tracker->tracker_id(), batch.get()); 1034 WriteToDatabase(batch.Pass(), callback); 1035 return; 1036 } 1037 1038 if (tracker->has_synced_details()) { 1039 // Check if the tracker was retitled. If it was, there should exist 1040 // another tracker for the new title, so delete old tracker. 1041 if (tracker->synced_details().title() != updated_details.title()) { 1042 RemoveTracker(tracker->tracker_id(), batch.get()); 1043 WriteToDatabase(batch.Pass(), callback); 1044 return; 1045 } 1046 } else { 1047 int64 parent_tracker_id = parent_tracker->tracker_id(); 1048 const std::string& title = updated_details.title(); 1049 TrackerSet* trackers = 1050 &trackers_by_parent_and_title_[parent_tracker_id][title]; 1051 1052 for (TrackerSet::iterator itr = trackers->begin(); 1053 itr != trackers->end(); ++itr) { 1054 if ((*itr)->file_id() == tracker->file_id()) { 1055 RemoveTracker(tracker->tracker_id(), batch.get()); 1056 WriteToDatabase(batch.Pass(), callback); 1057 return; 1058 } 1059 } 1060 1061 trackers_by_parent_and_title_[parent_tracker_id][std::string()].Erase( 1062 tracker); 1063 trackers->Insert(tracker); 1064 } 1065 } 1066 1067 *tracker->mutable_synced_details() = updated_details; 1068 1069 // Activate the tracker if: 1070 // - There is no active tracker that tracks |tracker->file_id()|. 1071 // - There is no active tracker that has the same |parent| and |title|. 1072 if (!tracker->active() && CanActivateTracker(*tracker)) 1073 MakeTrackerActive(tracker->tracker_id(), batch.get()); 1074 if (tracker->dirty() && !ShouldKeepDirty(*tracker)) { 1075 tracker->set_dirty(false); 1076 dirty_trackers_.erase(tracker); 1077 low_priority_dirty_trackers_.erase(tracker); 1078 } 1079 PutTrackerToBatch(*tracker, batch.get()); 1080 1081 WriteToDatabase(batch.Pass(), callback); 1082 } 1083 1084 bool MetadataDatabase::TryNoSideEffectActivation( 1085 int64 parent_tracker_id, 1086 const std::string& file_id, 1087 const SyncStatusCallback& callback) { 1088 DCHECK(ContainsKey(tracker_by_id_, parent_tracker_id)); 1089 DCHECK(ContainsKey(file_by_id_, file_id)); 1090 1091 FileMetadata file; 1092 if (!FindFileByFileID(file_id, &file)) { 1093 NOTREACHED(); 1094 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_FAILED)); 1095 return true; 1096 } 1097 std::string title = file.details().title(); 1098 DCHECK(!HasInvalidTitle(title)); 1099 1100 TrackerSet same_file_id; 1101 FindTrackersByFileID(file_id, &same_file_id); 1102 1103 FileTracker* tracker = NULL; 1104 for (TrackerSet::iterator itr = same_file_id.begin(); 1105 itr != same_file_id.end(); ++itr) { 1106 FileTracker* candidate = *itr; 1107 if (candidate->parent_tracker_id() != parent_tracker_id) 1108 continue; 1109 1110 if (candidate->has_synced_details() && 1111 candidate->synced_details().title() != title) 1112 continue; 1113 tracker = candidate; 1114 } 1115 1116 DCHECK(tracker); 1117 1118 if (!tracker->active()) { 1119 if (same_file_id.has_active()) 1120 return false; 1121 1122 TrackerSet same_title; 1123 FindTrackersByParentAndTitle(parent_tracker_id, title, &same_title); 1124 if (same_title.has_active()) 1125 return false; 1126 } 1127 1128 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 1129 if (!tracker->has_synced_details() || 1130 tracker->synced_details().title() != title) { 1131 trackers_by_parent_and_title_[parent_tracker_id] 1132 [GetTrackerTitle(*tracker)].Erase(tracker); 1133 trackers_by_parent_and_title_[parent_tracker_id][title].Insert( 1134 tracker); 1135 } 1136 *tracker->mutable_synced_details() = file.details(); 1137 1138 MakeTrackerActive(tracker->tracker_id(), batch.get()); 1139 tracker->set_dirty(false); 1140 dirty_trackers_.erase(tracker); 1141 low_priority_dirty_trackers_.erase(tracker); 1142 PutTrackerToBatch(*tracker, batch.get()); 1143 1144 WriteToDatabase(batch.Pass(), callback); 1145 return true; 1146 } 1147 1148 void MetadataDatabase::LowerTrackerPriority(int64 tracker_id) { 1149 TrackerByID::const_iterator found = tracker_by_id_.find(tracker_id); 1150 if (found == tracker_by_id_.end()) 1151 return; 1152 1153 FileTracker* tracker = found->second; 1154 if (dirty_trackers_.erase(tracker)) 1155 low_priority_dirty_trackers_.insert(tracker); 1156 } 1157 1158 void MetadataDatabase::PromoteLowerPriorityTrackersToNormal() { 1159 if (dirty_trackers_.empty()) { 1160 dirty_trackers_.swap(low_priority_dirty_trackers_); 1161 return; 1162 } 1163 dirty_trackers_.insert(low_priority_dirty_trackers_.begin(), 1164 low_priority_dirty_trackers_.end()); 1165 low_priority_dirty_trackers_.clear(); 1166 } 1167 1168 bool MetadataDatabase::GetNormalPriorityDirtyTracker( 1169 FileTracker* tracker) const { 1170 DirtyTrackers::const_iterator itr = dirty_trackers_.begin(); 1171 if (itr == dirty_trackers_.end()) 1172 return false; 1173 if (tracker) 1174 *tracker = **itr; 1175 return true; 1176 } 1177 1178 bool MetadataDatabase::GetLowPriorityDirtyTracker( 1179 FileTracker* tracker) const { 1180 DirtyTrackers::const_iterator itr = low_priority_dirty_trackers_.begin(); 1181 if (itr == low_priority_dirty_trackers_.end()) 1182 return false; 1183 if (tracker) 1184 *tracker = **itr; 1185 return true; 1186 } 1187 1188 bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id, 1189 TrackerSet* trackers) { 1190 DCHECK(file_id); 1191 DCHECK(trackers); 1192 // TODO(tzik): Make this function more efficient. 1193 for (TrackersByFileID::const_iterator itr = trackers_by_file_id_.begin(); 1194 itr != trackers_by_file_id_.end(); ++itr) { 1195 if (itr->second.size() > 1 && itr->second.has_active()) { 1196 *file_id = itr->first; 1197 *trackers = itr->second; 1198 return true; 1199 } 1200 } 1201 return false; 1202 } 1203 1204 bool MetadataDatabase::GetConflictingTrackers(TrackerSet* trackers) { 1205 DCHECK(trackers); 1206 // TODO(tzik): Make this function more efficient. 1207 for (TrackersByParentAndTitle::const_iterator parent_itr = 1208 trackers_by_parent_and_title_.begin(); 1209 parent_itr != trackers_by_parent_and_title_.end(); 1210 ++parent_itr) { 1211 const TrackersByTitle& trackers_by_title = parent_itr->second; 1212 for (TrackersByTitle::const_iterator itr = trackers_by_title.begin(); 1213 itr != trackers_by_title.end(); 1214 ++itr) { 1215 if (itr->second.size() > 1 && itr->second.has_active()) { 1216 *trackers = itr->second; 1217 return true; 1218 } 1219 } 1220 } 1221 return false; 1222 } 1223 1224 void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) { 1225 DCHECK(app_ids); 1226 app_ids->clear(); 1227 app_ids->reserve(app_root_by_app_id_.size()); 1228 for (TrackerByAppID::iterator itr = app_root_by_app_id_.begin(); 1229 itr != app_root_by_app_id_.end(); ++itr) { 1230 app_ids->push_back(itr->first); 1231 } 1232 } 1233 1234 MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner, 1235 const base::FilePath& database_path) 1236 : task_runner_(task_runner), 1237 database_path_(database_path), 1238 largest_known_change_id_(0), 1239 weak_ptr_factory_(this) { 1240 DCHECK(task_runner); 1241 } 1242 1243 // static 1244 void MetadataDatabase::CreateOnTaskRunner( 1245 base::SingleThreadTaskRunner* callback_runner, 1246 base::SequencedTaskRunner* task_runner, 1247 const base::FilePath& database_path, 1248 const CreateCallback& callback) { 1249 scoped_ptr<MetadataDatabase> metadata_database( 1250 new MetadataDatabase(task_runner, database_path)); 1251 SyncStatusCode status = 1252 metadata_database->InitializeOnTaskRunner(); 1253 if (status != SYNC_STATUS_OK) 1254 metadata_database.reset(); 1255 1256 callback_runner->PostTask(FROM_HERE, base::Bind( 1257 callback, status, base::Passed(&metadata_database))); 1258 } 1259 1260 // static 1261 SyncStatusCode MetadataDatabase::CreateForTesting( 1262 scoped_ptr<leveldb::DB> db, 1263 scoped_ptr<MetadataDatabase>* metadata_database_out) { 1264 scoped_ptr<MetadataDatabase> metadata_database( 1265 new MetadataDatabase(base::MessageLoopProxy::current(), 1266 base::FilePath())); 1267 metadata_database->db_ = db.Pass(); 1268 SyncStatusCode status = 1269 metadata_database->InitializeOnTaskRunner(); 1270 if (status == SYNC_STATUS_OK) 1271 *metadata_database_out = metadata_database.Pass(); 1272 return status; 1273 } 1274 1275 SyncStatusCode MetadataDatabase::InitializeOnTaskRunner() { 1276 base::ThreadRestrictions::AssertIOAllowed(); 1277 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 1278 1279 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 1280 bool created = false; 1281 // Open database unless |db_| is overridden for testing. 1282 if (!db_) { 1283 status = OpenDatabase(database_path_, &db_, &created); 1284 if (status != SYNC_STATUS_OK) 1285 return status; 1286 } 1287 1288 if (created) { 1289 status = WriteVersionInfo(db_.get()); 1290 if (status != SYNC_STATUS_OK) 1291 return status; 1292 } else { 1293 status = MigrateDatabaseIfNeeded(db_.get()); 1294 if (status != SYNC_STATUS_OK) 1295 return status; 1296 } 1297 1298 DatabaseContents contents; 1299 status = ReadDatabaseContents(db_.get(), &contents); 1300 if (status != SYNC_STATUS_OK) 1301 return status; 1302 1303 leveldb::WriteBatch batch; 1304 status = InitializeServiceMetadata(&contents, &batch); 1305 if (status != SYNC_STATUS_OK) 1306 return status; 1307 1308 status = RemoveUnreachableItems(&contents, &batch); 1309 if (status != SYNC_STATUS_OK) 1310 return status; 1311 1312 status = LevelDBStatusToSyncStatusCode( 1313 db_->Write(leveldb::WriteOptions(), &batch)); 1314 if (status != SYNC_STATUS_OK) 1315 return status; 1316 1317 BuildIndexes(&contents); 1318 return status; 1319 } 1320 1321 void MetadataDatabase::BuildIndexes(DatabaseContents* contents) { 1322 service_metadata_ = contents->service_metadata.Pass(); 1323 UpdateLargestKnownChangeID(service_metadata_->largest_change_id()); 1324 1325 for (ScopedVector<FileMetadata>::const_iterator itr = 1326 contents->file_metadata.begin(); 1327 itr != contents->file_metadata.end(); 1328 ++itr) { 1329 file_by_id_[(*itr)->file_id()] = *itr; 1330 } 1331 contents->file_metadata.weak_clear(); 1332 1333 for (ScopedVector<FileTracker>::const_iterator itr = 1334 contents->file_trackers.begin(); 1335 itr != contents->file_trackers.end(); 1336 ++itr) { 1337 FileTracker* tracker = *itr; 1338 tracker_by_id_[tracker->tracker_id()] = tracker; 1339 trackers_by_file_id_[tracker->file_id()].Insert(tracker); 1340 1341 if (IsAppRoot(*tracker)) 1342 app_root_by_app_id_[tracker->app_id()] = tracker; 1343 1344 if (tracker->parent_tracker_id()) { 1345 std::string title = GetTrackerTitle(*tracker); 1346 TrackerSet* trackers = 1347 &trackers_by_parent_and_title_[tracker->parent_tracker_id()][title]; 1348 trackers->Insert(tracker); 1349 } 1350 1351 if (tracker->dirty()) 1352 dirty_trackers_.insert(tracker); 1353 } 1354 contents->file_trackers.weak_clear(); 1355 } 1356 1357 void MetadataDatabase::RegisterTrackerAsAppRoot( 1358 const std::string& app_id, 1359 int64 tracker_id, 1360 leveldb::WriteBatch* batch) { 1361 FileTracker* tracker = tracker_by_id_[tracker_id]; 1362 DCHECK(tracker); 1363 tracker->set_app_id(app_id); 1364 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1365 app_root_by_app_id_[app_id] = tracker; 1366 1367 MakeTrackerActive(tracker->tracker_id(), batch); 1368 } 1369 1370 void MetadataDatabase::UnregisterTrackerAsAppRoot( 1371 const std::string& app_id, 1372 leveldb::WriteBatch* batch) { 1373 FileTracker* tracker = FindAndEraseItem(&app_root_by_app_id_, app_id); 1374 tracker->set_app_id(std::string()); 1375 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1376 1377 // Inactivate the tracker to drop all descendant. 1378 // (Note that we set tracker_kind to TRACKER_KIND_REGULAR before calling 1379 // this.) 1380 MakeTrackerInactive(tracker->tracker_id(), batch); 1381 } 1382 1383 void MetadataDatabase::MakeTrackerActive(int64 tracker_id, 1384 leveldb::WriteBatch* batch) { 1385 FileTracker* tracker = tracker_by_id_[tracker_id]; 1386 DCHECK(tracker); 1387 DCHECK(!tracker->active()); 1388 1389 int64 parent_tracker_id = tracker->parent_tracker_id(); 1390 DCHECK(tracker->has_synced_details()); 1391 trackers_by_file_id_[tracker->file_id()].Activate(tracker); 1392 if (parent_tracker_id) { 1393 trackers_by_parent_and_title_[parent_tracker_id][ 1394 tracker->synced_details().title()].Activate(tracker); 1395 } 1396 tracker->set_active(true); 1397 tracker->set_needs_folder_listing( 1398 tracker->synced_details().file_kind() == FILE_KIND_FOLDER); 1399 1400 // Make |tracker| a normal priority dirty tracker. 1401 if (tracker->dirty()) 1402 low_priority_dirty_trackers_.erase(tracker); 1403 tracker->set_dirty(true); 1404 dirty_trackers_.insert(tracker); 1405 1406 PutTrackerToBatch(*tracker, batch); 1407 } 1408 1409 void MetadataDatabase::MakeTrackerInactive(int64 tracker_id, 1410 leveldb::WriteBatch* batch) { 1411 FileTracker* tracker = tracker_by_id_[tracker_id]; 1412 DCHECK(tracker); 1413 DCHECK(tracker->active()); 1414 DCHECK_EQ(TRACKER_KIND_REGULAR, tracker->tracker_kind()); 1415 trackers_by_file_id_[tracker->file_id()].Inactivate(tracker); 1416 1417 std::string title = GetTrackerTitle(*tracker); 1418 int64 parent_tracker_id = tracker->parent_tracker_id(); 1419 if (parent_tracker_id) 1420 trackers_by_parent_and_title_[parent_tracker_id][title].Inactivate(tracker); 1421 tracker->set_active(false); 1422 1423 RemoveAllDescendantTrackers(tracker_id, batch); 1424 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1425 if (parent_tracker_id) 1426 MarkTrackersDirtyByPath(parent_tracker_id, title, batch); 1427 PutTrackerToBatch(*tracker, batch); 1428 } 1429 1430 void MetadataDatabase::MakeAppRootDisabled(int64 tracker_id, 1431 leveldb::WriteBatch* batch) { 1432 FileTracker* tracker = tracker_by_id_[tracker_id]; 1433 DCHECK(tracker); 1434 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind()); 1435 DCHECK(tracker->active()); 1436 1437 // Keep the app-root tracker active (but change the tracker_kind) so that 1438 // other conflicting trackers won't become active. 1439 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT); 1440 PutTrackerToBatch(*tracker, batch); 1441 } 1442 1443 void MetadataDatabase::MakeAppRootEnabled(int64 tracker_id, 1444 leveldb::WriteBatch* batch) { 1445 FileTracker* tracker = tracker_by_id_[tracker_id]; 1446 DCHECK(tracker); 1447 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind()); 1448 DCHECK(tracker->active()); 1449 1450 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1451 // Mark descendant trackers as dirty to handle changes in disable period. 1452 RecursiveMarkTrackerAsDirty(tracker_id, batch); 1453 PutTrackerToBatch(*tracker, batch); 1454 } 1455 1456 void MetadataDatabase::CreateTrackerForParentAndFileID( 1457 const FileTracker& parent_tracker, 1458 const std::string& file_id, 1459 leveldb::WriteBatch* batch) { 1460 CreateTrackerInternal(parent_tracker, file_id, NULL, batch); 1461 } 1462 1463 void MetadataDatabase::CreateTrackerForParentAndFileMetadata( 1464 const FileTracker& parent_tracker, 1465 const FileMetadata& file_metadata, 1466 leveldb::WriteBatch* batch) { 1467 DCHECK(file_metadata.has_details()); 1468 CreateTrackerInternal(parent_tracker, 1469 file_metadata.file_id(), 1470 &file_metadata.details(), 1471 batch); 1472 } 1473 1474 void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker, 1475 const std::string& file_id, 1476 const FileDetails* details, 1477 leveldb::WriteBatch* batch) { 1478 int64 tracker_id = GetNextTrackerID(batch); 1479 scoped_ptr<FileTracker> tracker(new FileTracker); 1480 tracker->set_tracker_id(tracker_id); 1481 tracker->set_parent_tracker_id(parent_tracker.tracker_id()); 1482 tracker->set_file_id(file_id); 1483 tracker->set_app_id(parent_tracker.app_id()); 1484 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1485 tracker->set_dirty(true); 1486 tracker->set_active(false); 1487 tracker->set_needs_folder_listing(false); 1488 if (details) { 1489 *tracker->mutable_synced_details() = *details; 1490 tracker->mutable_synced_details()->set_missing(true); 1491 tracker->mutable_synced_details()->clear_md5(); 1492 } 1493 PutTrackerToBatch(*tracker, batch); 1494 1495 trackers_by_file_id_[file_id].Insert(tracker.get()); 1496 // Note: |trackers_by_parent_and_title_| does not map from 1497 // FileMetadata::details but from FileTracker::synced_details, which is filled 1498 // on tracker updated phase. Use empty string as the title since 1499 // FileTracker::synced_details is empty here. 1500 std::string title; 1501 if (details) 1502 title = details->title(); 1503 trackers_by_parent_and_title_[parent_tracker.tracker_id()][title] 1504 .Insert(tracker.get()); 1505 dirty_trackers_.insert(tracker.get()); 1506 DCHECK(!ContainsKey(tracker_by_id_, tracker_id)); 1507 tracker_by_id_[tracker_id] = tracker.release(); 1508 } 1509 1510 void MetadataDatabase::RemoveTracker(int64 tracker_id, 1511 leveldb::WriteBatch* batch) { 1512 RemoveTrackerInternal(tracker_id, batch, false); 1513 RemoveAllDescendantTrackers(tracker_id, batch); 1514 } 1515 1516 void MetadataDatabase::RemoveTrackerIgnoringSameTitle( 1517 int64 tracker_id, 1518 leveldb::WriteBatch* batch) { 1519 RemoveTrackerInternal(tracker_id, batch, true); 1520 } 1521 1522 void MetadataDatabase::RemoveTrackerInternal( 1523 int64 tracker_id, 1524 leveldb::WriteBatch* batch, 1525 bool ignoring_same_title) { 1526 scoped_ptr<FileTracker> tracker( 1527 FindAndEraseItem(&tracker_by_id_, tracker_id)); 1528 if (!tracker) 1529 return; 1530 1531 EraseTrackerFromFileIDIndex(tracker.get(), batch); 1532 if (IsAppRoot(*tracker)) 1533 app_root_by_app_id_.erase(tracker->app_id()); 1534 EraseTrackerFromPathIndex(tracker.get()); 1535 dirty_trackers_.erase(tracker.get()); 1536 low_priority_dirty_trackers_.erase(tracker.get()); 1537 1538 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1539 if (!ignoring_same_title) { 1540 // Mark trackers having the same title with the given tracker as dirty. 1541 MarkTrackersDirtyByPath(tracker->parent_tracker_id(), 1542 GetTrackerTitle(*tracker), 1543 batch); 1544 } 1545 PutTrackerDeletionToBatch(tracker_id, batch); 1546 } 1547 1548 void MetadataDatabase::MaybeAddTrackersForNewFile( 1549 const FileMetadata& file, 1550 leveldb::WriteBatch* batch) { 1551 std::set<int64> parents_to_exclude; 1552 TrackersByFileID::iterator found = trackers_by_file_id_.find(file.file_id()); 1553 if (found != trackers_by_file_id_.end()) { 1554 for (TrackerSet::const_iterator itr = found->second.begin(); 1555 itr != found->second.end(); ++itr) { 1556 const FileTracker& tracker = **itr; 1557 int64 parent_tracker_id = tracker.parent_tracker_id(); 1558 if (!parent_tracker_id) 1559 continue; 1560 1561 // Exclude |parent_tracker_id| if it already has a tracker that has 1562 // unknown title or has the same title with |file|. 1563 if (!tracker.has_synced_details() || 1564 tracker.synced_details().title() == file.details().title()) { 1565 parents_to_exclude.insert(parent_tracker_id); 1566 } 1567 } 1568 } 1569 1570 for (int i = 0; i < file.details().parent_folder_ids_size(); ++i) { 1571 std::string parent_folder_id = file.details().parent_folder_ids(i); 1572 TrackersByFileID::iterator found = 1573 trackers_by_file_id_.find(parent_folder_id); 1574 if (found == trackers_by_file_id_.end()) 1575 continue; 1576 1577 for (TrackerSet::const_iterator itr = found->second.begin(); 1578 itr != found->second.end(); ++itr) { 1579 FileTracker* parent_tracker = *itr; 1580 int64 parent_tracker_id = parent_tracker->tracker_id(); 1581 if (!parent_tracker->active()) 1582 continue; 1583 1584 if (ContainsKey(parents_to_exclude, parent_tracker_id)) 1585 continue; 1586 1587 CreateTrackerForParentAndFileMetadata(*parent_tracker, file, batch); 1588 } 1589 } 1590 } 1591 1592 void MetadataDatabase::RemoveAllDescendantTrackers(int64 root_tracker_id, 1593 leveldb::WriteBatch* batch) { 1594 std::vector<int64> pending_trackers; 1595 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1596 root_tracker_id, 1597 std::back_inserter(pending_trackers)); 1598 1599 while (!pending_trackers.empty()) { 1600 int64 tracker_id = pending_trackers.back(); 1601 pending_trackers.pop_back(); 1602 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1603 tracker_id, 1604 std::back_inserter(pending_trackers)); 1605 RemoveTrackerIgnoringSameTitle(tracker_id, batch); 1606 } 1607 } 1608 1609 void MetadataDatabase::EraseTrackerFromFileIDIndex(FileTracker* tracker, 1610 leveldb::WriteBatch* batch) { 1611 TrackersByFileID::iterator found = 1612 trackers_by_file_id_.find(tracker->file_id()); 1613 if (found == trackers_by_file_id_.end()) 1614 return; 1615 1616 TrackerSet* trackers = &found->second; 1617 trackers->Erase(tracker); 1618 if (!trackers->tracker_set().empty()) 1619 return; 1620 trackers_by_file_id_.erase(found); 1621 EraseFileFromDatabase(tracker->file_id(), batch); 1622 } 1623 1624 void MetadataDatabase::EraseFileFromDatabase(const std::string& file_id, 1625 leveldb::WriteBatch* batch) { 1626 scoped_ptr<FileMetadata> file(FindAndEraseItem(&file_by_id_, file_id)); 1627 if (file) 1628 PutFileDeletionToBatch(file_id, batch); 1629 } 1630 1631 void MetadataDatabase::EraseTrackerFromPathIndex(FileTracker* tracker) { 1632 TrackersByParentAndTitle::iterator found = 1633 trackers_by_parent_and_title_.find(tracker->parent_tracker_id()); 1634 if (found == trackers_by_parent_and_title_.end()) 1635 return; 1636 1637 std::string title = GetTrackerTitle(*tracker); 1638 TrackersByTitle* trackers_by_title = &found->second; 1639 TrackersByTitle::iterator found_by_title = trackers_by_title->find(title); 1640 TrackerSet* conflicting_trackers = &found_by_title->second; 1641 conflicting_trackers->Erase(tracker); 1642 1643 if (conflicting_trackers->tracker_set().empty()) { 1644 trackers_by_title->erase(found_by_title); 1645 if (trackers_by_title->empty()) 1646 trackers_by_parent_and_title_.erase(found); 1647 } 1648 } 1649 1650 void MetadataDatabase::MarkSingleTrackerDirty(FileTracker* tracker, 1651 leveldb::WriteBatch* batch) { 1652 if (!tracker->dirty()) { 1653 tracker->set_dirty(true); 1654 PutTrackerToBatch(*tracker, batch); 1655 } 1656 dirty_trackers_.insert(tracker); 1657 low_priority_dirty_trackers_.erase(tracker); 1658 } 1659 1660 void MetadataDatabase::MarkTrackerSetDirty( 1661 TrackerSet* trackers, 1662 leveldb::WriteBatch* batch) { 1663 for (TrackerSet::iterator itr = trackers->begin(); 1664 itr != trackers->end(); ++itr) { 1665 MarkSingleTrackerDirty(*itr, batch); 1666 } 1667 } 1668 1669 void MetadataDatabase::MarkTrackersDirtyByFileID( 1670 const std::string& file_id, 1671 leveldb::WriteBatch* batch) { 1672 TrackersByFileID::iterator found = trackers_by_file_id_.find(file_id); 1673 if (found != trackers_by_file_id_.end()) 1674 MarkTrackerSetDirty(&found->second, batch); 1675 } 1676 1677 void MetadataDatabase::MarkTrackersDirtyByPath(int64 parent_tracker_id, 1678 const std::string& title, 1679 leveldb::WriteBatch* batch) { 1680 TrackersByParentAndTitle::iterator found = 1681 trackers_by_parent_and_title_.find(parent_tracker_id); 1682 if (found == trackers_by_parent_and_title_.end()) 1683 return; 1684 1685 TrackersByTitle::iterator itr = found->second.find(title); 1686 if (itr != found->second.end()) 1687 MarkTrackerSetDirty(&itr->second, batch); 1688 } 1689 1690 int64 MetadataDatabase::GetNextTrackerID(leveldb::WriteBatch* batch) { 1691 int64 tracker_id = service_metadata_->next_tracker_id(); 1692 service_metadata_->set_next_tracker_id(tracker_id + 1); 1693 PutServiceMetadataToBatch(*service_metadata_, batch); 1694 DCHECK_GT(tracker_id, 0); 1695 return tracker_id; 1696 } 1697 1698 void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id, 1699 leveldb::WriteBatch* batch) { 1700 std::vector<int64> stack; 1701 stack.push_back(root_tracker_id); 1702 while (!stack.empty()) { 1703 int64 tracker_id = stack.back(); 1704 stack.pop_back(); 1705 PushChildTrackersToContainer( 1706 trackers_by_parent_and_title_, tracker_id, std::back_inserter(stack)); 1707 1708 FileTracker* tracker = tracker_by_id_[tracker_id]; 1709 if (!tracker->dirty()) { 1710 tracker->set_dirty(true); 1711 PutTrackerToBatch(*tracker, batch); 1712 dirty_trackers_.insert(tracker); 1713 low_priority_dirty_trackers_.erase(tracker); 1714 } 1715 } 1716 } 1717 1718 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) { 1719 DCHECK(!tracker.active()); 1720 DCHECK_NE(service_metadata_->sync_root_tracker_id(), tracker.tracker_id()); 1721 1722 if (HasActiveTrackerForFileID(tracker.file_id())) 1723 return false; 1724 1725 if (tracker.app_id().empty() && 1726 tracker.tracker_id() != GetSyncRootTrackerID()) { 1727 return false; 1728 } 1729 1730 if (!tracker.has_synced_details()) 1731 return false; 1732 if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED) 1733 return false; 1734 if (HasInvalidTitle(tracker.synced_details().title())) 1735 return false; 1736 DCHECK(tracker.parent_tracker_id()); 1737 1738 return !HasActiveTrackerForPath(tracker.parent_tracker_id(), 1739 tracker.synced_details().title()); 1740 } 1741 1742 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { 1743 if (HasDisabledAppRoot(tracker)) 1744 return false; 1745 1746 DCHECK(tracker.dirty()); 1747 if (!tracker.has_synced_details()) 1748 return true; 1749 1750 FileByID::const_iterator found = file_by_id_.find(tracker.file_id()); 1751 if (found == file_by_id_.end()) 1752 return true; 1753 const FileMetadata* file = found->second; 1754 DCHECK(file); 1755 DCHECK(file->has_details()); 1756 1757 const FileDetails& local_details = tracker.synced_details(); 1758 const FileDetails& remote_details = file->details(); 1759 1760 if (tracker.active()) { 1761 if (tracker.needs_folder_listing()) 1762 return true; 1763 if (tracker.synced_details().md5() != file->details().md5()) 1764 return true; 1765 if (local_details.missing() != remote_details.missing()) 1766 return true; 1767 } 1768 1769 if (local_details.title() != remote_details.title()) 1770 return true; 1771 1772 return false; 1773 } 1774 1775 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const { 1776 TrackerByAppID::const_iterator found = 1777 app_root_by_app_id_.find(tracker.app_id()); 1778 if (found == app_root_by_app_id_.end()) 1779 return false; 1780 1781 const FileTracker* app_root_tracker = found->second; 1782 DCHECK(app_root_tracker); 1783 return app_root_tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 1784 } 1785 1786 bool MetadataDatabase::HasActiveTrackerForFileID( 1787 const std::string& file_id) const { 1788 TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id); 1789 return found != trackers_by_file_id_.end() && found->second.has_active(); 1790 } 1791 1792 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id, 1793 const std::string& title) const { 1794 TrackersByParentAndTitle::const_iterator found_by_parent = 1795 trackers_by_parent_and_title_.find(parent_tracker_id); 1796 if (found_by_parent == trackers_by_parent_and_title_.end()) 1797 return false; 1798 1799 const TrackersByTitle& trackers_by_title = found_by_parent->second; 1800 TrackersByTitle::const_iterator found = trackers_by_title.find(title); 1801 return found != trackers_by_title.end() && found->second.has_active(); 1802 } 1803 1804 void MetadataDatabase::UpdateByFileMetadata( 1805 const tracked_objects::Location& from_where, 1806 scoped_ptr<FileMetadata> file, 1807 leveldb::WriteBatch* batch) { 1808 DCHECK(file); 1809 DCHECK(file->has_details()); 1810 1811 DVLOG(1) << from_where.function_name() << ": " 1812 << file->file_id() << " (" 1813 << file->details().title() << ")" 1814 << (file->details().missing() ? " deleted" : ""); 1815 1816 std::string file_id = file->file_id(); 1817 if (file->details().missing()) { 1818 TrackerSet trackers; 1819 FindTrackersByFileID(file_id, &trackers); 1820 for (TrackerSet::const_iterator itr = trackers.begin(); 1821 itr != trackers.end(); ++itr) { 1822 const FileTracker& tracker = **itr; 1823 if (!tracker.has_synced_details() || 1824 tracker.synced_details().missing()) { 1825 RemoveTracker(tracker.tracker_id(), batch); 1826 } 1827 } 1828 } else { 1829 MaybeAddTrackersForNewFile(*file, batch); 1830 } 1831 1832 if (FindTrackersByFileID(file_id, NULL)) { 1833 MarkTrackersDirtyByFileID(file_id, batch); 1834 PutFileToBatch(*file, batch); 1835 FileMetadata* file_ptr = file.release(); 1836 std::swap(file_ptr, file_by_id_[file_id]); 1837 delete file_ptr; 1838 } 1839 } 1840 1841 void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 1842 const SyncStatusCallback& callback) { 1843 base::PostTaskAndReplyWithResult( 1844 task_runner_.get(), 1845 FROM_HERE, 1846 base::Bind(&leveldb::DB::Write, 1847 base::Unretained(db_.get()), 1848 leveldb::WriteOptions(), 1849 base::Owned(batch.release())), 1850 base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback)); 1851 } 1852 1853 scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles( 1854 const std::string& app_id) { 1855 scoped_ptr<base::ListValue> files(new base::ListValue); 1856 1857 FileTracker app_root_tracker; 1858 if (!FindAppRootTracker(app_id, &app_root_tracker)) 1859 return files.Pass(); 1860 1861 std::vector<int64> stack; 1862 PushChildTrackersToContainer( 1863 trackers_by_parent_and_title_, 1864 app_root_tracker.tracker_id(), 1865 std::back_inserter(stack)); 1866 while (!stack.empty()) { 1867 int64 tracker_id = stack.back(); 1868 stack.pop_back(); 1869 PushChildTrackersToContainer( 1870 trackers_by_parent_and_title_, tracker_id, std::back_inserter(stack)); 1871 1872 FileTracker* tracker = tracker_by_id_[tracker_id]; 1873 base::DictionaryValue* file = new DictionaryValue; 1874 1875 base::FilePath path = BuildDisplayPathForTracker(*tracker); 1876 file->SetString("path", path.AsUTF8Unsafe()); 1877 if (tracker->has_synced_details()) { 1878 file->SetString("title", tracker->synced_details().title()); 1879 file->SetString("type", 1880 FileKindToString(tracker->synced_details().file_kind())); 1881 } 1882 1883 base::DictionaryValue* details = new DictionaryValue; 1884 details->SetString("file_id", tracker->file_id()); 1885 if (tracker->has_synced_details() && 1886 tracker->synced_details().file_kind() == FILE_KIND_FILE) 1887 details->SetString("md5", tracker->synced_details().md5()); 1888 details->SetString("active", tracker->active() ? "true" : "false"); 1889 details->SetString("dirty", tracker->dirty() ? "true" : "false"); 1890 1891 file->Set("details", details); 1892 1893 files->Append(file); 1894 } 1895 1896 return files.Pass(); 1897 } 1898 1899 scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() { 1900 scoped_ptr<base::ListValue> list(new base::ListValue); 1901 list->Append(DumpTrackers().release()); 1902 list->Append(DumpMetadata().release()); 1903 return list.Pass(); 1904 } 1905 1906 bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id, 1907 int64 change_id) { 1908 FileByID::const_iterator found = file_by_id_.find(file_id); 1909 if (found == file_by_id_.end()) 1910 return false; 1911 DCHECK(found->second->has_details()); 1912 return found->second->details().change_id() >= change_id; 1913 } 1914 1915 scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() { 1916 scoped_ptr<base::ListValue> trackers(new base::ListValue); 1917 1918 // Append the first element for metadata. 1919 base::DictionaryValue* metadata = new DictionaryValue; 1920 const char *trackerKeys[] = { 1921 "tracker_id", "path", "file_id", "tracker_kind", "app_id", 1922 "active", "dirty", "folder_listing", 1923 "title", "kind", "md5", "etag", "missing", "change_id", 1924 }; 1925 std::vector<std::string> key_strings( 1926 trackerKeys, trackerKeys + ARRAYSIZE_UNSAFE(trackerKeys)); 1927 base::ListValue* keys = new ListValue; 1928 keys->AppendStrings(key_strings); 1929 metadata->SetString("title", "Trackers"); 1930 metadata->Set("keys", keys); 1931 trackers->Append(metadata); 1932 1933 // Append tracker data. 1934 for (TrackerByID::const_iterator itr = tracker_by_id_.begin(); 1935 itr != tracker_by_id_.end(); ++itr) { 1936 const FileTracker& tracker = *itr->second; 1937 base::DictionaryValue* dict = new DictionaryValue; 1938 base::FilePath path = BuildDisplayPathForTracker(tracker); 1939 dict->SetString("tracker_id", base::Int64ToString(tracker.tracker_id())); 1940 dict->SetString("path", path.AsUTF8Unsafe()); 1941 dict->SetString("file_id", tracker.file_id()); 1942 TrackerKind tracker_kind = tracker.tracker_kind(); 1943 dict->SetString( 1944 "tracker_kind", 1945 tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" : 1946 tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" : 1947 tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" : 1948 "Regular"); 1949 dict->SetString("app_id", tracker.app_id()); 1950 dict->SetString("active", tracker.active() ? "true" : "false"); 1951 dict->SetString("dirty", tracker.dirty() ? "true" : "false"); 1952 dict->SetString("folder_listing", 1953 tracker.needs_folder_listing() ? "needed" : "no"); 1954 if (tracker.has_synced_details()) { 1955 const FileDetails& details = tracker.synced_details(); 1956 dict->SetString("title", details.title()); 1957 dict->SetString("kind", FileKindToString(details.file_kind())); 1958 dict->SetString("md5", details.md5()); 1959 dict->SetString("etag", details.etag()); 1960 dict->SetString("missing", details.missing() ? "true" : "false"); 1961 dict->SetString("change_id", base::Int64ToString(details.change_id())); 1962 } 1963 trackers->Append(dict); 1964 } 1965 return trackers.Pass(); 1966 } 1967 1968 scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() { 1969 scoped_ptr<base::ListValue> files(new base::ListValue); 1970 1971 // Append the first element for metadata. 1972 base::DictionaryValue* metadata = new DictionaryValue; 1973 const char *fileKeys[] = { 1974 "file_id", "title", "type", "md5", "etag", "missing", 1975 "change_id", "parents" 1976 }; 1977 std::vector<std::string> key_strings( 1978 fileKeys, fileKeys + ARRAYSIZE_UNSAFE(fileKeys)); 1979 base::ListValue* keys = new ListValue; 1980 keys->AppendStrings(key_strings); 1981 metadata->SetString("title", "Metadata"); 1982 metadata->Set("keys", keys); 1983 files->Append(metadata); 1984 1985 // Append metadata data. 1986 for (FileByID::const_iterator itr = file_by_id_.begin(); 1987 itr != file_by_id_.end(); ++itr) { 1988 const FileMetadata& file = *itr->second; 1989 1990 base::DictionaryValue* dict = new DictionaryValue; 1991 dict->SetString("file_id", file.file_id()); 1992 if (file.has_details()) { 1993 const FileDetails& details = file.details(); 1994 dict->SetString("title", details.title()); 1995 dict->SetString("type", FileKindToString(details.file_kind())); 1996 dict->SetString("md5", details.md5()); 1997 dict->SetString("etag", details.etag()); 1998 dict->SetString("missing", details.missing() ? "true" : "false"); 1999 dict->SetString("change_id", base::Int64ToString(details.change_id())); 2000 2001 std::vector<std::string> parents; 2002 for (int i = 0; i < details.parent_folder_ids_size(); ++i) 2003 parents.push_back(details.parent_folder_ids(i)); 2004 dict->SetString("parents", JoinString(parents, ",")); 2005 } 2006 files->Append(dict); 2007 } 2008 return files.Pass(); 2009 } 2010 2011 } // namespace drive_backend 2012 } // namespace sync_file_system 2013