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