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