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