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