metadata_database.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 ScopedVector<google_apis::ChangeResource> changes, 739 const SyncStatusCallback& callback) { 740 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 741 742 for (ScopedVector<google_apis::ChangeResource>::const_iterator itr = 743 changes.begin(); 744 itr != changes.end(); 745 ++itr) { 746 const google_apis::ChangeResource& change = **itr; 747 scoped_ptr<FileMetadata> file( 748 CreateFileMetadataFromChangeResource(change)); 749 std::string file_id = file->file_id(); 750 751 MarkTrackersDirtyByFileID(file_id, batch.get()); 752 if (!file->details().deleted()) 753 MaybeAddTrackersForNewFile(*file, batch.get()); 754 755 if (FindTrackersByFileID(file_id, NULL)) { 756 PutFileToBatch(*file, batch.get()); 757 758 // Set |file| to |file_by_id_[file_id]| and delete old value. 759 FileMetadata* file_ptr = file.release(); 760 std::swap(file_ptr, file_by_id_[file_id]); 761 delete file_ptr; 762 } 763 } 764 765 WriteToDatabase(batch.Pass(), callback); 766} 767 768void MetadataDatabase::UpdateByFileResource( 769 int64 change_id, 770 const google_apis::FileResource& resource, 771 const SyncStatusCallback& callback) { 772 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 773 774 scoped_ptr<FileMetadata> file( 775 CreateFileMetadataFromFileResource(change_id, resource)); 776 std::string file_id = file->file_id(); 777 778 // TODO(tzik): Consolidate with UpdateByChangeList. 779 MarkTrackersDirtyByFileID(file_id, batch.get()); 780 if (!file->details().deleted()) { 781 MaybeAddTrackersForNewFile(*file, batch.get()); 782 783 if (FindTrackersByFileID(file_id, NULL)) { 784 PutFileToBatch(*file, batch.get()); 785 786 // Set |file| to |file_by_id_[file_id]| and delete old value. 787 FileMetadata* file_ptr = file.release(); 788 std::swap(file_ptr, file_by_id_[file_id]); 789 delete file_ptr; 790 } 791 } 792 793 WriteToDatabase(batch.Pass(), callback); 794} 795 796void MetadataDatabase::PopulateFolderByChildList( 797 const std::string& folder_id, 798 const FileIDList& child_file_ids, 799 const SyncStatusCallback& callback) { 800 TrackerSet trackers; 801 if (!FindTrackersByFileID(folder_id, &trackers) || 802 !trackers.has_active()) { 803 // It's OK that there is no folder to populate its children. 804 // Inactive folders should ignore their contents updates. 805 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 806 return; 807 } 808 809 FileTracker* folder_tracker = 810 tracker_by_id_[trackers.active_tracker()->tracker_id()]; 811 DCHECK(folder_tracker); 812 std::set<std::string> children(child_file_ids.begin(), child_file_ids.end()); 813 814 std::vector<int64> known_children; 815 PushChildTrackersToContainer(trackers_by_parent_and_title_, 816 folder_tracker->tracker_id(), 817 std::back_inserter(known_children)); 818 for (std::vector<int64>::iterator itr = known_children.begin(); 819 itr != known_children.end(); ++itr) 820 children.erase(tracker_by_id_[*itr]->file_id()); 821 822 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 823 for (FileIDList::const_iterator itr = child_file_ids.begin(); 824 itr != child_file_ids.end(); ++itr) 825 CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get()); 826 folder_tracker->set_needs_folder_listing(false); 827 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker)) { 828 folder_tracker->set_dirty(false); 829 dirty_trackers_.erase(folder_tracker); 830 } 831 PutTrackerToBatch(*folder_tracker, batch.get()); 832 833 WriteToDatabase(batch.Pass(), callback); 834} 835 836void MetadataDatabase::UpdateTracker(int64 tracker_id, 837 const FileDetails& updated_details, 838 const SyncStatusCallback& callback) { 839 TrackerByID::iterator found = tracker_by_id_.find(tracker_id); 840 if (found == tracker_by_id_.end()) { 841 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 842 return; 843 } 844 845 FileTracker* tracker = found->second; 846 DCHECK(tracker); 847 848 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 849 850 if (updated_details.deleted()) { 851 // The update deletes the local file. 852 FileByID::iterator found = file_by_id_.find(tracker->file_id()); 853 if (found == file_by_id_.end() || found->second->details().deleted()) { 854 // Both the tracker and metadata have the deleted flag, now it's safe to 855 // delete the |tracker|. 856 RemoveTracker(tracker->tracker_id(), batch.get()); 857 } else { 858 // The local file is deleted, but corresponding remote file isn't. 859 // Put the tracker back to the initial state. 860 tracker->clear_synced_details(); 861 tracker->set_dirty(true); 862 tracker->set_active(false); 863 PutTrackerToBatch(*tracker, batch.get()); 864 } 865 866 WriteToDatabase(batch.Pass(), callback); 867 return; 868 } 869 870 // Check if the tracker was retitled. If it was, update the title and its 871 // index in advance. 872 if (!tracker->has_synced_details() || 873 tracker->synced_details().title() != updated_details.title()) { 874 UpdateTrackerTitle(tracker, updated_details.title(), batch.get()); 875 } 876 877 *tracker->mutable_synced_details() = updated_details; 878 879 // Activate the tracker if: 880 // - There is no active tracker that tracks |tracker->file_id()|. 881 // - There is no active tracker that has the same |parent| and |title|. 882 if (!tracker->active() && CanActivateTracker(*tracker)) 883 MakeTrackerActive(tracker->tracker_id(), batch.get()); 884 if (tracker->dirty() && !ShouldKeepDirty(*tracker)) { 885 tracker->set_dirty(false); 886 dirty_trackers_.erase(tracker); 887 } 888 PutTrackerToBatch(*tracker, batch.get()); 889 890 WriteToDatabase(batch.Pass(), callback); 891} 892 893MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner) 894 : task_runner_(task_runner), weak_ptr_factory_(this) { 895 DCHECK(task_runner); 896} 897 898// static 899void MetadataDatabase::CreateOnTaskRunner( 900 base::SingleThreadTaskRunner* callback_runner, 901 base::SequencedTaskRunner* task_runner, 902 const base::FilePath& database_path, 903 const CreateCallback& callback) { 904 scoped_ptr<MetadataDatabase> metadata_database( 905 new MetadataDatabase(task_runner)); 906 SyncStatusCode status = 907 metadata_database->InitializeOnTaskRunner(database_path); 908 if (status != SYNC_STATUS_OK) 909 metadata_database.reset(); 910 911 callback_runner->PostTask(FROM_HERE, base::Bind( 912 callback, status, base::Passed(&metadata_database))); 913} 914 915// static 916SyncStatusCode MetadataDatabase::CreateForTesting( 917 scoped_ptr<leveldb::DB> db, 918 scoped_ptr<MetadataDatabase>* metadata_database_out) { 919 scoped_ptr<MetadataDatabase> metadata_database( 920 new MetadataDatabase(base::MessageLoopProxy::current())); 921 metadata_database->db_ = db.Pass(); 922 SyncStatusCode status = 923 metadata_database->InitializeOnTaskRunner(base::FilePath()); 924 if (status == SYNC_STATUS_OK) 925 *metadata_database_out = metadata_database.Pass(); 926 return status; 927} 928 929SyncStatusCode MetadataDatabase::InitializeOnTaskRunner( 930 const base::FilePath& database_path) { 931 base::ThreadRestrictions::AssertIOAllowed(); 932 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 933 934 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 935 bool created = false; 936 // Open database unless |db_| is overridden for testing. 937 if (!db_) { 938 status = OpenDatabase(database_path, &db_, &created); 939 if (status != SYNC_STATUS_OK) 940 return status; 941 } 942 943 if (created) { 944 status = WriteVersionInfo(db_.get()); 945 if (status != SYNC_STATUS_OK) 946 return status; 947 } else { 948 status = MigrateDatabaseIfNeeded(db_.get()); 949 if (status != SYNC_STATUS_OK) 950 return status; 951 } 952 953 DatabaseContents contents; 954 status = ReadDatabaseContents(db_.get(), &contents); 955 if (status != SYNC_STATUS_OK) 956 return status; 957 958 leveldb::WriteBatch batch; 959 status = InitializeServiceMetadata(&contents, &batch); 960 if (status != SYNC_STATUS_OK) 961 return status; 962 963 status = RemoveUnreachableItems(&contents, &batch); 964 if (status != SYNC_STATUS_OK) 965 return status; 966 967 status = LevelDBStatusToSyncStatusCode( 968 db_->Write(leveldb::WriteOptions(), &batch)); 969 if (status != SYNC_STATUS_OK) 970 return status; 971 972 BuildIndexes(&contents); 973 return status; 974} 975 976void MetadataDatabase::BuildIndexes(DatabaseContents* contents) { 977 service_metadata_ = contents->service_metadata.Pass(); 978 979 for (ScopedVector<FileMetadata>::const_iterator itr = 980 contents->file_metadata.begin(); 981 itr != contents->file_metadata.end(); 982 ++itr) { 983 file_by_id_[(*itr)->file_id()] = *itr; 984 } 985 contents->file_metadata.weak_clear(); 986 987 for (ScopedVector<FileTracker>::const_iterator itr = 988 contents->file_trackers.begin(); 989 itr != contents->file_trackers.end(); 990 ++itr) { 991 FileTracker* tracker = *itr; 992 tracker_by_id_[tracker->tracker_id()] = tracker; 993 trackers_by_file_id_[tracker->file_id()].Insert(tracker); 994 995 if (IsAppRoot(*tracker)) 996 app_root_by_app_id_[tracker->app_id()] = tracker; 997 998 if (tracker->parent_tracker_id()) { 999 std::string title = GetTrackerTitle(*tracker); 1000 TrackerSet* trackers = 1001 &trackers_by_parent_and_title_[tracker->parent_tracker_id()][title]; 1002 trackers->Insert(tracker); 1003 } 1004 1005 if (tracker->dirty()) 1006 dirty_trackers_.insert(tracker); 1007 } 1008 contents->file_trackers.weak_clear(); 1009} 1010 1011void MetadataDatabase::RegisterTrackerAsAppRoot( 1012 const std::string& app_id, 1013 int64 tracker_id, 1014 leveldb::WriteBatch* batch) { 1015 FileTracker* tracker = tracker_by_id_[tracker_id]; 1016 DCHECK(tracker); 1017 tracker->set_app_id(app_id); 1018 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1019 app_root_by_app_id_[app_id] = tracker; 1020 1021 MakeTrackerActive(tracker->tracker_id(), batch); 1022} 1023 1024void MetadataDatabase::UnregisterTrackerAsAppRoot( 1025 const std::string& app_id, 1026 leveldb::WriteBatch* batch) { 1027 FileTracker* tracker = FindAndEraseItem(&app_root_by_app_id_, app_id); 1028 tracker->set_app_id(std::string()); 1029 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1030 1031 // Inactivate the tracker to drop all descendant. 1032 // (Note that we set tracker_kind to TRACKER_KIND_REGULAR before calling 1033 // this.) 1034 MakeTrackerInactive(tracker->tracker_id(), batch); 1035} 1036 1037void MetadataDatabase::MakeTrackerActive(int64 tracker_id, 1038 leveldb::WriteBatch* batch) { 1039 FileTracker* tracker = tracker_by_id_[tracker_id]; 1040 DCHECK(tracker); 1041 DCHECK(!tracker->active()); 1042 1043 int64 parent_tracker_id = tracker->parent_tracker_id(); 1044 DCHECK(tracker->has_synced_details()); 1045 trackers_by_file_id_[tracker->file_id()].Activate(tracker); 1046 if (parent_tracker_id) { 1047 trackers_by_parent_and_title_[parent_tracker_id][ 1048 tracker->synced_details().title()].Activate(tracker); 1049 } 1050 tracker->set_active(true); 1051 tracker->set_needs_folder_listing( 1052 tracker->synced_details().file_kind() == FILE_KIND_FOLDER); 1053 tracker->set_dirty(true); 1054 dirty_trackers_.insert(tracker); 1055 1056 PutTrackerToBatch(*tracker, batch); 1057} 1058 1059void MetadataDatabase::MakeTrackerInactive(int64 tracker_id, 1060 leveldb::WriteBatch* batch) { 1061 FileTracker* tracker = tracker_by_id_[tracker_id]; 1062 DCHECK(tracker); 1063 DCHECK(tracker->active()); 1064 DCHECK_EQ(TRACKER_KIND_REGULAR, tracker->tracker_kind()); 1065 trackers_by_file_id_[tracker->file_id()].Inactivate(tracker); 1066 1067 std::string title = GetTrackerTitle(*tracker); 1068 int64 parent_tracker_id = tracker->parent_tracker_id(); 1069 if (parent_tracker_id) 1070 trackers_by_parent_and_title_[parent_tracker_id][title].Inactivate(tracker); 1071 tracker->set_active(false); 1072 1073 RemoveAllDescendantTrackers(tracker_id, batch); 1074 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1075 if (parent_tracker_id) 1076 MarkTrackersDirtyByPath(parent_tracker_id, title, batch); 1077 PutTrackerToBatch(*tracker, batch); 1078} 1079 1080void MetadataDatabase::MakeAppRootDisabled(int64 tracker_id, 1081 leveldb::WriteBatch* batch) { 1082 FileTracker* tracker = tracker_by_id_[tracker_id]; 1083 DCHECK(tracker); 1084 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind()); 1085 DCHECK(tracker->active()); 1086 1087 // Keep the app-root tracker active (but change the tracker_kind) so that 1088 // other conflicting trackers won't become active. 1089 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT); 1090 PutTrackerToBatch(*tracker, batch); 1091} 1092 1093void MetadataDatabase::MakeAppRootEnabled(int64 tracker_id, 1094 leveldb::WriteBatch* batch) { 1095 FileTracker* tracker = tracker_by_id_[tracker_id]; 1096 DCHECK(tracker); 1097 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind()); 1098 DCHECK(tracker->active()); 1099 1100 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT); 1101 // Mark descendant trackers as dirty to handle changes in disable period. 1102 RecursiveMarkTrackerAsDirty(tracker_id, batch); 1103 PutTrackerToBatch(*tracker, batch); 1104} 1105 1106void MetadataDatabase::CreateTrackerForParentAndFileID( 1107 const FileTracker& parent_tracker, 1108 const std::string& file_id, 1109 leveldb::WriteBatch* batch) { 1110 int64 tracker_id = GetNextTrackerID(batch); 1111 scoped_ptr<FileTracker> tracker(new FileTracker); 1112 tracker->set_tracker_id(tracker_id); 1113 tracker->set_parent_tracker_id(parent_tracker.tracker_id()); 1114 tracker->set_file_id(file_id); 1115 tracker->set_app_id(parent_tracker.app_id()); 1116 tracker->set_tracker_kind(TRACKER_KIND_REGULAR); 1117 tracker->set_dirty(true); 1118 tracker->set_active(false); 1119 tracker->set_needs_folder_listing(false); 1120 PutTrackerToBatch(*tracker, batch); 1121 1122 trackers_by_file_id_[file_id].Insert(tracker.get()); 1123 // Note: |trackers_by_parent_and_title_| does not map from 1124 // FileMetadata::details but from FileTracker::synced_details, which is filled 1125 // on tracker updated phase. Use empty string as the title since 1126 // FileTracker::synced_details is empty here. 1127 trackers_by_parent_and_title_[parent_tracker.tracker_id()][std::string()] 1128 .Insert(tracker.get()); 1129 dirty_trackers_.insert(tracker.get()); 1130 DCHECK(!ContainsKey(tracker_by_id_, tracker_id)); 1131 tracker_by_id_[tracker_id] = tracker.release(); 1132} 1133 1134void MetadataDatabase::RemoveTracker(int64 tracker_id, 1135 leveldb::WriteBatch* batch) { 1136 RemoveTrackerInternal(tracker_id, batch, false); 1137} 1138 1139void MetadataDatabase::RemoveTrackerIgnoringSiblings( 1140 int64 tracker_id, 1141 leveldb::WriteBatch* batch) { 1142 RemoveTrackerInternal(tracker_id, batch, true); 1143} 1144 1145void MetadataDatabase::RemoveTrackerInternal( 1146 int64 tracker_id, 1147 leveldb::WriteBatch* batch, 1148 bool ignoring_siblings) { 1149 scoped_ptr<FileTracker> tracker( 1150 FindAndEraseItem(&tracker_by_id_, tracker_id)); 1151 if (!tracker) 1152 return; 1153 1154 EraseTrackerFromFileIDIndex(tracker.get(), batch); 1155 if (IsAppRoot(*tracker)) 1156 app_root_by_app_id_.erase(tracker->app_id()); 1157 EraseTrackerFromPathIndex(tracker.get()); 1158 1159 MarkTrackersDirtyByFileID(tracker->file_id(), batch); 1160 if (!ignoring_siblings) { 1161 MarkTrackersDirtyByPath(tracker->parent_tracker_id(), 1162 GetTrackerTitle(*tracker), 1163 batch); 1164 } 1165 PutTrackerDeletionToBatch(tracker_id, batch); 1166} 1167 1168void MetadataDatabase::MaybeAddTrackersForNewFile( 1169 const FileMetadata& file, 1170 leveldb::WriteBatch* batch) { 1171 std::set<int64> known_parents; 1172 TrackersByFileID::iterator found = trackers_by_file_id_.find(file.file_id()); 1173 if (found != trackers_by_file_id_.end()) { 1174 for (TrackerSet::const_iterator itr = found->second.begin(); 1175 itr != found->second.end(); ++itr) { 1176 int64 parent_tracker_id = (*itr)->parent_tracker_id(); 1177 if (parent_tracker_id) 1178 known_parents.insert(parent_tracker_id); 1179 } 1180 } 1181 1182 for (int i = 0; i < file.details().parent_folder_ids_size(); ++i) { 1183 std::string parent_folder_id = file.details().parent_folder_ids(i); 1184 TrackersByFileID::iterator found = 1185 trackers_by_file_id_.find(parent_folder_id); 1186 if (found == trackers_by_file_id_.end()) 1187 continue; 1188 1189 for (TrackerSet::const_iterator itr = found->second.begin(); 1190 itr != found->second.end(); ++itr) { 1191 FileTracker* parent_tracker = *itr; 1192 int64 parent_tracker_id = parent_tracker->tracker_id(); 1193 if (!parent_tracker->active()) 1194 continue; 1195 1196 if (ContainsKey(known_parents, parent_tracker_id)) 1197 continue; 1198 1199 CreateTrackerForParentAndFileID(*parent_tracker, file.file_id(), batch); 1200 } 1201 } 1202} 1203 1204void MetadataDatabase::RemoveAllDescendantTrackers(int64 root_tracker_id, 1205 leveldb::WriteBatch* batch) { 1206 std::vector<int64> pending_trackers; 1207 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1208 root_tracker_id, 1209 std::back_inserter(pending_trackers)); 1210 1211 while (!pending_trackers.empty()) { 1212 int64 tracker_id = pending_trackers.back(); 1213 pending_trackers.pop_back(); 1214 PushChildTrackersToContainer(trackers_by_parent_and_title_, 1215 tracker_id, 1216 std::back_inserter(pending_trackers)); 1217 RemoveTrackerIgnoringSiblings(tracker_id, batch); 1218 } 1219} 1220 1221void MetadataDatabase::EraseTrackerFromFileIDIndex(FileTracker* tracker, 1222 leveldb::WriteBatch* batch) { 1223 TrackersByFileID::iterator found = 1224 trackers_by_file_id_.find(tracker->file_id()); 1225 if (found == trackers_by_file_id_.end()) 1226 return; 1227 1228 TrackerSet* trackers = &found->second; 1229 trackers->Erase(tracker); 1230 if (!trackers->tracker_set().empty()) 1231 return; 1232 trackers_by_file_id_.erase(found); 1233 EraseFileFromDatabase(tracker->file_id(), batch); 1234} 1235 1236void MetadataDatabase::EraseFileFromDatabase(const std::string& file_id, 1237 leveldb::WriteBatch* batch) { 1238 scoped_ptr<FileMetadata> file(FindAndEraseItem(&file_by_id_, file_id)); 1239 if (file) 1240 PutFileDeletionToBatch(file_id, batch); 1241} 1242 1243void MetadataDatabase::EraseTrackerFromPathIndex(FileTracker* tracker) { 1244 TrackersByParentAndTitle::iterator found = 1245 trackers_by_parent_and_title_.find(tracker->parent_tracker_id()); 1246 if (found == trackers_by_parent_and_title_.end()) 1247 return; 1248 1249 std::string title = GetTrackerTitle(*tracker); 1250 TrackersByTitle* trackers_by_title = &found->second; 1251 TrackersByTitle::iterator found_by_title = trackers_by_title->find(title); 1252 TrackerSet* conflicting_trackers = &found_by_title->second; 1253 conflicting_trackers->Erase(tracker); 1254 1255 if (conflicting_trackers->tracker_set().empty()) { 1256 trackers_by_title->erase(found_by_title); 1257 if (trackers_by_title->empty()) 1258 trackers_by_parent_and_title_.erase(found); 1259 } 1260} 1261 1262void MetadataDatabase::MarkTrackerSetDirty( 1263 TrackerSet* trackers, 1264 leveldb::WriteBatch* batch) { 1265 for (TrackerSet::iterator itr = trackers->begin(); 1266 itr != trackers->end(); ++itr) { 1267 FileTracker* tracker = *itr; 1268 if (tracker->dirty()) 1269 continue; 1270 tracker->set_dirty(true); 1271 PutTrackerToBatch(*tracker, batch); 1272 dirty_trackers_.insert(tracker); 1273 } 1274} 1275 1276void MetadataDatabase::MarkTrackersDirtyByFileID( 1277 const std::string& file_id, 1278 leveldb::WriteBatch* batch) { 1279 TrackersByFileID::iterator found = trackers_by_file_id_.find(file_id); 1280 if (found != trackers_by_file_id_.end()) 1281 MarkTrackerSetDirty(&found->second, batch); 1282} 1283 1284void MetadataDatabase::MarkTrackersDirtyByPath(int64 parent_tracker_id, 1285 const std::string& title, 1286 leveldb::WriteBatch* batch) { 1287 TrackersByParentAndTitle::iterator found = 1288 trackers_by_parent_and_title_.find(parent_tracker_id); 1289 if (found == trackers_by_parent_and_title_.end()) { 1290 NOTREACHED() << "parent: " << parent_tracker_id 1291 << ", title: " << title; 1292 return; 1293 } 1294 1295 TrackersByTitle::iterator itr = found->second.find(title); 1296 if (itr != found->second.end()) 1297 MarkTrackerSetDirty(&itr->second, batch); 1298} 1299 1300int64 MetadataDatabase::GetNextTrackerID(leveldb::WriteBatch* batch) { 1301 int64 tracker_id = service_metadata_->next_tracker_id(); 1302 service_metadata_->set_next_tracker_id(tracker_id + 1); 1303 PutServiceMetadataToBatch(*service_metadata_, batch); 1304 DCHECK_GT(tracker_id, 0); 1305 return tracker_id; 1306} 1307 1308void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id, 1309 leveldb::WriteBatch* batch) { 1310 std::vector<int64> stack; 1311 stack.push_back(root_tracker_id); 1312 while (!stack.empty()) { 1313 int64 tracker_id = stack.back(); 1314 stack.pop_back(); 1315 PushChildTrackersToContainer( 1316 trackers_by_parent_and_title_, tracker_id, std::back_inserter(stack)); 1317 1318 FileTracker* tracker = tracker_by_id_[tracker_id]; 1319 if (!tracker->dirty()) { 1320 tracker->set_dirty(true); 1321 PutTrackerToBatch(*tracker, batch); 1322 dirty_trackers_.insert(tracker); 1323 } 1324 } 1325} 1326 1327bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) { 1328 DCHECK(!tracker.active()); 1329 DCHECK_NE(service_metadata_->sync_root_tracker_id(), tracker.tracker_id()); 1330 1331 if (HasActiveTrackerForFileID(tracker.file_id())) 1332 return false; 1333 1334 if (tracker.app_id().empty()) 1335 return false; 1336 if (!tracker.has_synced_details()) 1337 return false; 1338 DCHECK(tracker.parent_tracker_id()); 1339 1340 return !HasActiveTrackerForPath(tracker.parent_tracker_id(), 1341 tracker.synced_details().title()); 1342} 1343 1344bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { 1345 if (HasDisabledAppRoot(tracker)) 1346 return false; 1347 1348 DCHECK(tracker.dirty()); 1349 if (!tracker.has_synced_details()) 1350 return true; 1351 1352 FileByID::const_iterator found = file_by_id_.find(tracker.file_id()); 1353 if (found == file_by_id_.end()) 1354 return true; 1355 const FileMetadata* file = found->second; 1356 DCHECK(file); 1357 1358 if (tracker.active()) { 1359 if (tracker.needs_folder_listing()) 1360 return true; 1361 if (tracker.synced_details().md5() != file->details().md5()) 1362 return true; 1363 } 1364 1365 const FileDetails& local_details = tracker.synced_details(); 1366 const FileDetails& remote_details = file->details(); 1367 1368 if (local_details.title() != remote_details.title()) 1369 return true; 1370 if (local_details.deleted() != remote_details.deleted()) 1371 return true; 1372 1373 return false; 1374} 1375 1376bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const { 1377 TrackerByAppID::const_iterator found = 1378 app_root_by_app_id_.find(tracker.app_id()); 1379 if (found == app_root_by_app_id_.end()) 1380 return false; 1381 1382 const FileTracker* app_root_tracker = found->second; 1383 DCHECK(app_root_tracker); 1384 return app_root_tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 1385} 1386 1387bool MetadataDatabase::HasActiveTrackerForFileID( 1388 const std::string& file_id) const { 1389 TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id); 1390 return found != trackers_by_file_id_.end() && found->second.has_active(); 1391} 1392 1393bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id, 1394 const std::string& title) const { 1395 TrackersByParentAndTitle::const_iterator found_by_parent = 1396 trackers_by_parent_and_title_.find(parent_tracker_id); 1397 if (found_by_parent == trackers_by_parent_and_title_.end()) 1398 return false; 1399 1400 const TrackersByTitle& trackers_by_title = found_by_parent->second; 1401 TrackersByTitle::const_iterator found = trackers_by_title.find(title); 1402 return found != trackers_by_title.end() && found->second.has_active(); 1403} 1404 1405void MetadataDatabase::UpdateTrackerTitle(FileTracker* tracker, 1406 const std::string& new_title, 1407 leveldb::WriteBatch* batch) { 1408 int64 parent_id = tracker->parent_tracker_id(); 1409 std::string old_title = GetTrackerTitle(*tracker); 1410 DCHECK_NE(old_title, new_title); 1411 DCHECK(!new_title.empty()); 1412 1413 TrackersByTitle* trackers_by_title = 1414 &trackers_by_parent_and_title_[parent_id]; 1415 TrackerSet* old_siblings = &(*trackers_by_title)[old_title]; 1416 TrackerSet* new_siblings = &(*trackers_by_title)[new_title]; 1417 1418 old_siblings->Erase(tracker); 1419 if (old_siblings->empty()) 1420 trackers_by_title->erase(old_title); 1421 else 1422 MarkTrackerSetDirty(old_siblings, batch); 1423 1424 if (tracker->active() && new_siblings->has_active()) { 1425 // Inactivate existing active tracker. 1426 FileTracker* obstacle = new_siblings->active_tracker(); 1427 new_siblings->Inactivate(obstacle); 1428 DCHECK_EQ(TRACKER_KIND_REGULAR, obstacle->tracker_kind()); 1429 1430 TrackerSet* same_file_id_trackers_to_obstacle = 1431 &trackers_by_file_id_[obstacle->file_id()]; 1432 same_file_id_trackers_to_obstacle->Inactivate(obstacle); 1433 MarkTrackerSetDirty(same_file_id_trackers_to_obstacle, batch); 1434 1435 obstacle->set_active(false); 1436 PutTrackerToBatch(*obstacle, batch); 1437 1438 RemoveAllDescendantTrackers(obstacle->tracker_id(), batch); 1439 } 1440 1441 tracker->mutable_synced_details()->set_title(new_title); 1442 new_siblings->Insert(tracker); 1443 PutTrackerToBatch(*tracker, batch); 1444} 1445 1446void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 1447 const SyncStatusCallback& callback) { 1448 base::PostTaskAndReplyWithResult( 1449 task_runner_.get(), 1450 FROM_HERE, 1451 base::Bind(&leveldb::DB::Write, 1452 base::Unretained(db_.get()), 1453 leveldb::WriteOptions(), 1454 base::Owned(batch.release())), 1455 base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback)); 1456} 1457 1458} // namespace drive_backend 1459} // namespace sync_file_system 1460