metadata_database.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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 <stack> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/files/file_path.h" 12#include "base/location.h" 13#include "base/memory/scoped_vector.h" 14#include "base/message_loop/message_loop_proxy.h" 15#include "base/sequenced_task_runner.h" 16#include "base/stl_util.h" 17#include "base/strings/string_number_conversions.h" 18#include "base/strings/string_util.h" 19#include "base/strings/stringprintf.h" 20#include "base/task_runner_util.h" 21#include "base/threading/thread_restrictions.h" 22#include "chrome/browser/google_apis/drive_api_parser.h" 23#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 24#include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h" 25#include "chrome/browser/sync_file_system/logger.h" 26#include "third_party/leveldatabase/src/include/leveldb/db.h" 27#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 28#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h" 29#include "webkit/common/fileapi/file_system_util.h" 30 31namespace sync_file_system { 32namespace drive_backend { 33 34typedef MetadataDatabase::FileByAppID FileByAppID; 35typedef MetadataDatabase::FileByFileID FileByFileID; 36typedef MetadataDatabase::FileByParentAndTitle FileByParentAndTitle; 37typedef MetadataDatabase::FileSet FileSet; 38typedef MetadataDatabase::FilesByParent FilesByParent; 39 40const char kDatabaseVersionKey[] = "VERSION"; 41const int64 kCurrentDatabaseVersion = 3; 42const char kServiceMetadataKey[] = "SERVICE"; 43const char kFileMetadataKeyPrefix[] = "FILE: "; 44 45struct DatabaseContents { 46 scoped_ptr<ServiceMetadata> service_metadata; 47 ScopedVector<DriveFileMetadata> file_metadata; 48}; 49 50namespace { 51 52std::string RemovePrefix(const std::string& str, const std::string& prefix) { 53 if (StartsWithASCII(str, prefix, true)) 54 return str.substr(prefix.size()); 55 return str; 56} 57 58void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback, 59 const leveldb::Status& status) { 60 callback.Run(LevelDBStatusToSyncStatusCode(status)); 61} 62 63void PutFileToBatch(const DriveFileMetadata& file, leveldb::WriteBatch* batch) { 64 std::string value; 65 bool success = file.SerializeToString(&value); 66 DCHECK(success); 67 batch->Put(kFileMetadataKeyPrefix + file.file_id(), value); 68} 69 70void PushChildrenToStack(const FilesByParent& files_by_parent, 71 const std::string& folder_id, 72 std::stack<std::string>* stack) { 73 FilesByParent::const_iterator found = files_by_parent.find(folder_id); 74 if (found == files_by_parent.end()) 75 return; 76 const FileSet& children = found->second; 77 for (FileSet::const_iterator itr = children.begin(); 78 itr != children.end(); ++itr) 79 stack->push((*itr)->file_id()); 80} 81 82// Returns true if |db| has no content. 83bool IsDatabaseEmpty(leveldb::DB* db) { 84 DCHECK(db); 85 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 86 itr->SeekToFirst(); 87 return !itr->Valid(); 88} 89 90SyncStatusCode OpenDatabase(const base::FilePath& path, 91 scoped_ptr<leveldb::DB>* db_out, 92 bool* created) { 93 base::ThreadRestrictions::AssertIOAllowed(); 94 DCHECK(db_out); 95 DCHECK(created); 96 97 leveldb::Options options; 98 options.max_open_files = 0; // Use minimum. 99 options.create_if_missing = true; 100 leveldb::DB* db = NULL; 101 leveldb::Status db_status = 102 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db); 103 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status); 104 if (status != SYNC_STATUS_OK) { 105 delete db; 106 return status; 107 } 108 109 *created = IsDatabaseEmpty(db); 110 db_out->reset(db); 111 return status; 112} 113 114SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) { 115 base::ThreadRestrictions::AssertIOAllowed(); 116 DCHECK(db); 117 std::string value; 118 leveldb::Status status = 119 db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); 120 int64 version = 0; 121 if (status.ok()) { 122 if (!base::StringToInt64(value, &version)) 123 return SYNC_DATABASE_ERROR_FAILED; 124 } else { 125 if (!status.IsNotFound()) 126 return SYNC_DATABASE_ERROR_FAILED; 127 } 128 129 switch (version) { 130 case 0: 131 drive_backend::MigrateDatabaseFromV0ToV1(db); 132 // fall-through 133 case 1: 134 drive_backend::MigrateDatabaseFromV1ToV2(db); 135 // fall-through 136 case 2: 137 // TODO(tzik): Migrate database from version 2 to 3. 138 // * Add sync-root folder as active, dirty and needs_folder_listing 139 // folder. 140 // * Add app-root folders for each origins. Each app-root folder for 141 // an enabled origin should be a active, dirty and 142 // needs_folder_listing folder. And Each app-root folder for a 143 // disabled origin should be an inactive, dirty and 144 // non-needs_folder_listing folder. 145 // * Add a file metadata for each file in previous version. 146 NOTIMPLEMENTED(); 147 return SYNC_DATABASE_ERROR_FAILED; 148 // fall-through 149 case 3: 150 DCHECK_EQ(3, kCurrentDatabaseVersion); 151 return SYNC_STATUS_OK; 152 default: 153 return SYNC_DATABASE_ERROR_FAILED; 154 } 155} 156 157SyncStatusCode WriteVersionInfo(leveldb::DB* db) { 158 base::ThreadRestrictions::AssertIOAllowed(); 159 DCHECK(db); 160 return LevelDBStatusToSyncStatusCode( 161 db->Put(leveldb::WriteOptions(), 162 kDatabaseVersionKey, 163 base::Int64ToString(kCurrentDatabaseVersion))); 164} 165 166SyncStatusCode ReadDatabaseContents(leveldb::DB* db, 167 DatabaseContents* contents) { 168 base::ThreadRestrictions::AssertIOAllowed(); 169 DCHECK(db); 170 DCHECK(contents); 171 172 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions())); 173 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) { 174 std::string key = itr->key().ToString(); 175 std::string value = itr->value().ToString(); 176 if (key == kServiceMetadataKey) { 177 scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata); 178 if (!service_metadata->ParseFromString(value)) { 179 util::Log(logging::LOG_WARNING, FROM_HERE, 180 "Failed to parse SyncServiceMetadata"); 181 continue; 182 } 183 184 contents->service_metadata = service_metadata.Pass(); 185 continue; 186 } 187 188 if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) { 189 std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix); 190 191 scoped_ptr<DriveFileMetadata> metadata(new DriveFileMetadata); 192 if (!metadata->ParseFromString(itr->value().ToString())) { 193 util::Log(logging::LOG_WARNING, FROM_HERE, 194 "Failed to parse a Metadata"); 195 continue; 196 } 197 198 contents->file_metadata.push_back(metadata.release()); 199 continue; 200 } 201 } 202 203 return SYNC_STATUS_OK; 204} 205 206SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents, 207 leveldb::WriteBatch* batch) { 208 209 if (!contents->service_metadata) { 210 contents->service_metadata.reset(new ServiceMetadata); 211 212 std::string value; 213 contents->service_metadata->SerializeToString(&value); 214 batch->Put(kServiceMetadataKey, value); 215 } 216 return SYNC_STATUS_OK; 217} 218 219SyncStatusCode RemoveUnreachableFiles(DatabaseContents* contents, 220 leveldb::WriteBatch* batch) { 221 FileByFileID unvisited_files; 222 FilesByParent files_by_parent; 223 224 for (ScopedVector<DriveFileMetadata>::iterator itr = 225 contents->file_metadata.begin(); 226 itr != contents->file_metadata.end(); 227 ++itr) { 228 DriveFileMetadata* metadata = *itr; 229 DCHECK(!ContainsKey(unvisited_files, metadata->file_id())); 230 unvisited_files[metadata->file_id()] = metadata; 231 files_by_parent[metadata->parent_folder_id()].insert(metadata); 232 } 233 234 // Traverse synced metadata tree. Take only active items and their children. 235 // Drop unreachable items. 236 ScopedVector<DriveFileMetadata> reachable_files; 237 std::stack<std::string> pending; 238 if (!contents->service_metadata->sync_root_folder_id().empty()) 239 pending.push(contents->service_metadata->sync_root_folder_id()); 240 241 while (!pending.empty()) { 242 std::string file_id = pending.top(); 243 pending.pop(); 244 245 { 246 FileByFileID::iterator found = unvisited_files.find(file_id); 247 if (found == unvisited_files.end()) 248 continue; 249 250 DriveFileMetadata* metadata = found->second; 251 unvisited_files.erase(found); 252 reachable_files.push_back(metadata); 253 254 if (!metadata->active()) 255 continue; 256 } 257 258 FilesByParent::iterator found = files_by_parent.find(file_id); 259 if (found == files_by_parent.end()) 260 continue; 261 262 for (FileSet::const_iterator itr = found->second.begin(); 263 itr != found->second.end(); 264 ++itr) 265 pending.push((*itr)->file_id()); 266 } 267 268 for (FileByFileID::iterator itr = unvisited_files.begin(); 269 itr != unvisited_files.end(); 270 ++itr) { 271 DriveFileMetadata* metadata = itr->second; 272 batch->Delete(metadata->file_id()); 273 delete metadata; 274 } 275 unvisited_files.clear(); 276 277 // |reachable_files| contains all files/folders reachable from sync-root 278 // folder via active folders. 279 contents->file_metadata.weak_clear(); 280 contents->file_metadata.swap(reachable_files); 281 282 return SYNC_STATUS_OK; 283} 284 285template <typename Container, typename Key, typename Value> 286bool FindItem(const Container& container, const Key& key, Value* value) { 287 typename Container::const_iterator found = container.find(key); 288 if (found == container.end()) 289 return false; 290 if (value) 291 *value = *found->second; 292 return true; 293} 294 295void RunSoon(const tracked_objects::Location& from_here, 296 const base::Closure& closure) { 297 base::MessageLoopProxy::current()->PostTask(from_here, closure); 298} 299 300} // namespace 301 302bool MetadataDatabase::FileIDComparator::operator()( 303 DriveFileMetadata* left, 304 DriveFileMetadata* right) const { 305 return left->file_id() < right->file_id(); 306} 307 308// static 309void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner, 310 const base::FilePath& database_path, 311 const CreateCallback& callback) { 312 task_runner->PostTask(FROM_HERE, base::Bind( 313 &CreateOnTaskRunner, 314 base::MessageLoopProxy::current(), 315 make_scoped_refptr(task_runner), 316 database_path, callback)); 317} 318 319MetadataDatabase::~MetadataDatabase() { 320 task_runner_->DeleteSoon(FROM_HERE, db_.release()); 321 STLDeleteContainerPairSecondPointers( 322 file_by_file_id_.begin(), file_by_file_id_.end()); 323} 324 325int64 MetadataDatabase::GetLargestChangeID() const { 326 return service_metadata_->largest_change_id(); 327} 328 329void MetadataDatabase::RegisterApp(const std::string& app_id, 330 const std::string& folder_id, 331 const SyncStatusCallback& callback) { 332 if (FindAppRootFolder(app_id, NULL)) { 333 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 334 return; 335 } 336 337 DriveFileMetadata folder; 338 if (!FindFileByFileID(folder_id, &folder)) { 339 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 340 return; 341 } 342 343 DCHECK(!folder.active()); 344 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 345 RegisterFolderAsAppRoot(app_id, folder.file_id(), batch.get()); 346 WriteToDatabase(batch.Pass(), callback); 347} 348 349void MetadataDatabase::DisableApp(const std::string& app_id, 350 const SyncStatusCallback& callback) { 351 DriveFileMetadata folder; 352 if (!FindAppRootFolder(app_id, &folder)) { 353 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 354 return; 355 } 356 357 if (!folder.active()) { 358 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 359 return; 360 } 361 362 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 363 MakeFileInactive(folder.file_id(), batch.get()); 364 WriteToDatabase(batch.Pass(), callback); 365} 366 367void MetadataDatabase::EnableApp(const std::string& app_id, 368 const SyncStatusCallback& callback) { 369 DriveFileMetadata folder; 370 if (!FindAppRootFolder(app_id, &folder)) { 371 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND)); 372 return; 373 } 374 375 if (folder.active()) { 376 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 377 return; 378 } 379 380 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 381 MakeFileActive(folder.file_id(), batch.get()); 382 WriteToDatabase(batch.Pass(), callback); 383} 384 385void MetadataDatabase::UnregisterApp(const std::string& app_id, 386 const SyncStatusCallback& callback) { 387 DriveFileMetadata folder; 388 if (!FindAppRootFolder(app_id, &folder)) { 389 RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 390 return; 391 } 392 393 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 394 UnregisterFolderAsAppRoot(app_id, batch.get()); 395 WriteToDatabase(batch.Pass(), callback); 396} 397 398bool MetadataDatabase::FindAppRootFolder(const std::string& app_id, 399 DriveFileMetadata* folder) const { 400 return FindItem(app_root_by_app_id_, app_id, folder); 401} 402 403bool MetadataDatabase::FindFileByFileID(const std::string& file_id, 404 DriveFileMetadata* metadata) const { 405 return FindItem(file_by_file_id_, file_id, metadata); 406} 407 408size_t MetadataDatabase::FindFilesByParentAndTitle( 409 const std::string& file_id, 410 const std::string& title, 411 ScopedVector<DriveFileMetadata>* files) const { 412 NOTIMPLEMENTED(); 413 return 0; 414} 415 416bool MetadataDatabase::FindActiveFileByParentAndTitle( 417 const std::string& folder_id, 418 const std::string& title, 419 DriveFileMetadata* file) const { 420 return FindItem(active_file_by_parent_and_title_, 421 std::make_pair(folder_id, title), 422 file); 423} 424 425bool MetadataDatabase::FindActiveFileByPath(const std::string& app_id, 426 const base::FilePath& path, 427 DriveFileMetadata* file) const { 428 DriveFileMetadata current; 429 if (!FindAppRootFolder(app_id, ¤t)) 430 return false; 431 432 std::vector<base::FilePath::StringType> components; 433 path.GetComponents(&components); 434 435 std::string parent_folder_id = current.file_id(); 436 for (std::vector<base::FilePath::StringType>::iterator itr = 437 components.begin(); 438 itr != components.end(); 439 ++itr) { 440 std::string current_folder_id = current.file_id(); 441 if (!FindActiveFileByParentAndTitle( 442 current_folder_id, base::FilePath(*itr).AsUTF8Unsafe(), ¤t)) 443 return false; 444 } 445 if (file) 446 *file = current; 447 return true; 448} 449 450bool MetadataDatabase::BuildPathForFile(const std::string& file_id, 451 base::FilePath* path) const { 452 NOTIMPLEMENTED(); 453 return false; 454} 455 456void MetadataDatabase::UpdateByChangeList( 457 ScopedVector<google_apis::ChangeResource> changes, 458 const SyncStatusCallback& callback) { 459 NOTIMPLEMENTED(); 460} 461 462void MetadataDatabase::PopulateFolder( 463 const std::string& folder_id, 464 ScopedVector<google_apis::ResourceEntry> children, 465 const SyncStatusCallback& callback) { 466 NOTIMPLEMENTED(); 467} 468 469MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner) 470 : task_runner_(task_runner), weak_ptr_factory_(this) { 471 DCHECK(task_runner); 472} 473 474// static 475void MetadataDatabase::CreateOnTaskRunner( 476 base::SingleThreadTaskRunner* callback_runner, 477 base::SequencedTaskRunner* task_runner, 478 const base::FilePath& database_path, 479 const CreateCallback& callback) { 480 scoped_ptr<MetadataDatabase> metadata_database( 481 new MetadataDatabase(task_runner)); 482 SyncStatusCode status = 483 metadata_database->InitializeOnTaskRunner(database_path); 484 if (status != SYNC_STATUS_OK) 485 metadata_database.reset(); 486 487 callback_runner->PostTask(FROM_HERE, base::Bind( 488 callback, status, base::Passed(&metadata_database))); 489} 490 491// static 492SyncStatusCode MetadataDatabase::CreateForTesting( 493 scoped_ptr<leveldb::DB> db, 494 scoped_ptr<MetadataDatabase>* metadata_database_out) { 495 scoped_ptr<MetadataDatabase> metadata_database( 496 new MetadataDatabase(base::MessageLoopProxy::current())); 497 metadata_database->db_ = db.Pass(); 498 SyncStatusCode status = 499 metadata_database->InitializeOnTaskRunner(base::FilePath()); 500 if (status == SYNC_STATUS_OK) 501 *metadata_database_out = metadata_database.Pass(); 502 return status; 503} 504 505SyncStatusCode MetadataDatabase::InitializeOnTaskRunner( 506 const base::FilePath& database_path) { 507 base::ThreadRestrictions::AssertIOAllowed(); 508 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 509 510 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 511 bool created = false; 512 // Open database unless |db_| is overridden for testing. 513 if (!db_) { 514 status = OpenDatabase(database_path, &db_, &created); 515 if (status != SYNC_STATUS_OK) 516 return status; 517 } 518 519 if (created) { 520 status = WriteVersionInfo(db_.get()); 521 if (status != SYNC_STATUS_OK) 522 return status; 523 } else { 524 status = MigrateDatabaseIfNeeded(db_.get()); 525 if (status != SYNC_STATUS_OK) 526 return status; 527 } 528 529 DatabaseContents contents; 530 status = ReadDatabaseContents(db_.get(), &contents); 531 if (status != SYNC_STATUS_OK) 532 return status; 533 534 leveldb::WriteBatch batch; 535 status = InitializeServiceMetadata(&contents, &batch); 536 if (status != SYNC_STATUS_OK) 537 return status; 538 539 status = RemoveUnreachableFiles(&contents, &batch); 540 if (status != SYNC_STATUS_OK) 541 return status; 542 543 status = LevelDBStatusToSyncStatusCode( 544 db_->Write(leveldb::WriteOptions(), &batch)); 545 if (status != SYNC_STATUS_OK) 546 return status; 547 548 BuildIndexes(&contents); 549 return status; 550} 551 552void MetadataDatabase::BuildIndexes(DatabaseContents* contents) { 553 for (ScopedVector<DriveFileMetadata>::iterator itr = 554 contents->file_metadata.begin(); 555 itr != contents->file_metadata.end(); 556 ++itr) { 557 DriveFileMetadata* file = *itr; 558 file_by_file_id_[file->file_id()] = file; 559 560 if (file->is_app_root()) 561 app_root_by_app_id_[file->app_id()] = file; 562 563 if (file->active() && file->has_synced_details()) { 564 FileByParentAndTitle::key_type key( 565 file->parent_folder_id(), file->synced_details().title()); 566 active_file_by_parent_and_title_[key] = file; 567 } 568 569 if (!file->parent_folder_id().empty()) 570 files_by_parent_[file->parent_folder_id()].insert(file); 571 572 if (file->dirty()) 573 dirty_files_.insert(file); 574 } 575 576 contents->file_metadata.weak_clear(); 577} 578 579void MetadataDatabase::RegisterFolderAsAppRoot( 580 const std::string& app_id, 581 const std::string& folder_id, 582 leveldb::WriteBatch* batch) { 583 DriveFileMetadata* folder = file_by_file_id_[folder_id]; 584 if (!folder || folder->active() || folder->is_app_root() || 585 !folder->app_id().empty()) { 586 NOTREACHED(); 587 return; 588 } 589 590 folder->set_is_app_root(true); 591 folder->set_app_id(app_id); 592 folder->set_active(true); 593 folder->set_dirty(true); 594 folder->set_needs_folder_listing(true); 595 PutFileToBatch(*folder, batch); 596 597 app_root_by_app_id_[app_id] = folder; 598 if (folder->has_synced_details()) { 599 FileByParentAndTitle::key_type key( 600 folder->parent_folder_id(), folder->synced_details().title()); 601 DCHECK(!ContainsKey(active_file_by_parent_and_title_, key)); 602 active_file_by_parent_and_title_[key] = folder; 603 } 604 dirty_files_.insert(folder); 605} 606 607void MetadataDatabase::UnregisterFolderAsAppRoot( 608 const std::string& app_id, 609 leveldb::WriteBatch* batch) { 610 DriveFileMetadata* folder = app_root_by_app_id_[app_id]; 611 if (!folder || !folder->active() || 612 folder->app_id() != app_id || !folder->is_app_root()) { 613 NOTREACHED(); 614 return; 615 } 616 617 folder->set_active(false); 618 folder->set_is_app_root(false); 619 folder->set_app_id(std::string()); 620 PutFileToBatch(*folder, batch); 621 622 // Remove child entries recursively. 623 std::stack<std::string> pending_files; 624 PushChildrenToStack(files_by_parent_, folder->file_id(), &pending_files); 625 while (!pending_files.empty()) { 626 std::string file_id = pending_files.top(); 627 pending_files.pop(); 628 PushChildrenToStack(files_by_parent_, file_id, &pending_files); 629 RemoveFile(file_id, batch); 630 } 631 632 app_root_by_app_id_.erase(app_id); 633 if (folder->has_synced_details()) { 634 FileByParentAndTitle::key_type key( 635 folder->parent_folder_id(), folder->synced_details().title()); 636 active_file_by_parent_and_title_.erase(key); 637 } 638} 639 640void MetadataDatabase::MakeFileActive(const std::string& file_id, 641 leveldb::WriteBatch* batch) { 642 DriveFileMetadata* file = file_by_file_id_[file_id]; 643 if (!file || file->active()) { 644 NOTREACHED(); 645 return; 646 } 647 648 file->set_active(true); 649 if (file->has_synced_details() && 650 file->synced_details().kind() == KIND_FOLDER) 651 file->set_needs_folder_listing(true); 652 PutFileToBatch(*file, batch); 653 654 if (file->has_synced_details()) { 655 FileByParentAndTitle::key_type key( 656 file->parent_folder_id(), file->synced_details().title()); 657 DCHECK(!ContainsKey(active_file_by_parent_and_title_, key)); 658 active_file_by_parent_and_title_[key] = file; 659 } 660} 661 662void MetadataDatabase::MakeFileInactive(const std::string& file_id, 663 leveldb::WriteBatch* batch) { 664 DriveFileMetadata* file = file_by_file_id_[file_id]; 665 if (!file || !file->active()) { 666 NOTREACHED(); 667 return; 668 } 669 670 file->set_active(false); 671 PutFileToBatch(*file, batch); 672 673 if (file->has_synced_details()) { 674 FileByParentAndTitle::key_type key( 675 file->parent_folder_id(), file->synced_details().title()); 676 DCHECK(ContainsKey(active_file_by_parent_and_title_, key)); 677 active_file_by_parent_and_title_.erase(key); 678 } 679} 680 681void MetadataDatabase::RemoveFile(const std::string& file_id, 682 leveldb::WriteBatch* batch) { 683 scoped_ptr<DriveFileMetadata> file(file_by_file_id_[file_id]); 684 file_by_file_id_.erase(file_id); 685 686 batch->Delete(file->file_id()); 687 688 files_by_parent_[file->parent_folder_id()].erase(file.get()); 689 if (file->is_app_root()) 690 app_root_by_app_id_.erase(file->app_id()); 691 if (file->active() && file->has_synced_details()) { 692 FileByParentAndTitle::key_type key( 693 file->parent_folder_id(), file->synced_details().title()); 694 active_file_by_parent_and_title_.erase(key); 695 } 696 dirty_files_.erase(file.get()); 697} 698 699void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, 700 const SyncStatusCallback& callback) { 701 base::PostTaskAndReplyWithResult( 702 task_runner_.get(), 703 FROM_HERE, 704 base::Bind(&leveldb::DB::Write, 705 base::Unretained(db_.get()), 706 leveldb::WriteOptions(), 707 base::Owned(batch.release())), 708 base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback)); 709} 710 711} // namespace drive_backend 712} // namespace sync_file_system 713