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