| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/gdata/gdata_files.h" | 5 #include "chrome/browser/chromeos/gdata/gdata_files.h" |
| 6 | 6 |
| 7 #include <leveldb/db.h> | 7 #include <leveldb/db.h> |
| 8 | 8 |
| 9 #include "base/message_loop_proxy.h" | 9 #include "base/message_loop_proxy.h" |
| 10 #include "base/platform_file.h" | 10 #include "base/platform_file.h" |
| 11 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
| 12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 13 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
| 14 #include "base/sequenced_task_runner.h" | 14 #include "base/sequenced_task_runner.h" |
| 15 #include "base/tracked_objects.h" | 15 #include "base/tracked_objects.h" |
| 16 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
| 17 #include "chrome/browser/chromeos/gdata/gdata.pb.h" | 17 #include "chrome/browser/chromeos/gdata/gdata.pb.h" |
| 18 #include "chrome/browser/chromeos/gdata/gdata_directory_service.h" |
| 18 #include "chrome/browser/chromeos/gdata/gdata_util.h" | 19 #include "chrome/browser/chromeos/gdata/gdata_util.h" |
| 19 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" | 20 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" |
| 20 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
| 21 #include "net/base/escape.h" | 22 #include "net/base/escape.h" |
| 22 | 23 |
| 23 using content::BrowserThread; | 24 using content::BrowserThread; |
| 24 | 25 |
| 25 namespace gdata { | 26 namespace gdata { |
| 26 namespace { | 27 namespace { |
| 27 | 28 |
| 28 const char kSlash[] = "/"; | 29 const char kSlash[] = "/"; |
| 29 const char kEscapedSlash[] = "\xE2\x88\x95"; | 30 const char kEscapedSlash[] = "\xE2\x88\x95"; |
| 30 | 31 |
| 31 // m: prefix for filesystem metadata db keys, version and largest_changestamp. | |
| 32 // r: prefix for resource id db keys. | |
| 33 const char kDBKeyLargestChangestamp[] = "m:largest_changestamp"; | |
| 34 const char kDBKeyVersion[] = "m:version"; | |
| 35 const char kDBKeyResourceIdPrefix[] = "r:"; | |
| 36 | |
| 37 // Extracts resource_id out of edit url. | 32 // Extracts resource_id out of edit url. |
| 38 std::string ExtractResourceId(const GURL& url) { | 33 std::string ExtractResourceId(const GURL& url) { |
| 39 return net::UnescapeURLComponent(url.ExtractFileName(), | 34 return net::UnescapeURLComponent(url.ExtractFileName(), |
| 40 net::UnescapeRule::URL_SPECIAL_CHARS); | 35 net::UnescapeRule::URL_SPECIAL_CHARS); |
| 41 } | 36 } |
| 42 | 37 |
| 43 // Returns true if |proto| is a valid proto as the root directory. | |
| 44 // Used to reject incompatible proto. | |
| 45 bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) { | |
| 46 const GDataEntryProto& entry_proto = proto.gdata_entry(); | |
| 47 // The title field for the root directory was originally empty, then | |
| 48 // changed to "gdata", then changed to "drive". Discard the proto data if | |
| 49 // the older formats are detected. See crbug.com/128133 for details. | |
| 50 if (entry_proto.title() != "drive") { | |
| 51 LOG(ERROR) << "Incompatible proto detected (bad title): " | |
| 52 << entry_proto.title(); | |
| 53 return false; | |
| 54 } | |
| 55 // The title field for the root directory was originally empty. Discard | |
| 56 // the proto data if the older format is detected. | |
| 57 if (entry_proto.resource_id() != kGDataRootDirectoryResourceId) { | |
| 58 LOG(ERROR) << "Incompatible proto detected (bad resource ID): " | |
| 59 << entry_proto.resource_id(); | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 return true; | |
| 64 } | |
| 65 | |
| 66 } // namespace | 38 } // namespace |
| 67 | 39 |
| 68 EntryInfoResult::EntryInfoResult() : error(GDATA_FILE_ERROR_FAILED) { | |
| 69 } | |
| 70 | |
| 71 EntryInfoResult::~EntryInfoResult() { | |
| 72 } | |
| 73 | |
| 74 EntryInfoPairResult::EntryInfoPairResult() { | |
| 75 } | |
| 76 | |
| 77 EntryInfoPairResult::~EntryInfoPairResult() { | |
| 78 } | |
| 79 | |
| 80 // GDataEntry class. | 40 // GDataEntry class. |
| 81 | 41 |
| 82 GDataEntry::GDataEntry(GDataDirectoryService* directory_service) | 42 GDataEntry::GDataEntry(GDataDirectoryService* directory_service) |
| 83 : parent_(NULL), | 43 : parent_(NULL), |
| 84 directory_service_(directory_service), | 44 directory_service_(directory_service), |
| 85 deleted_(false) { | 45 deleted_(false) { |
| 86 } | 46 } |
| 87 | 47 |
| 88 GDataEntry::~GDataEntry() { | 48 GDataEntry::~GDataEntry() { |
| 89 } | 49 } |
| (...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 GDataDirectory* dir = iter->second; | 345 GDataDirectory* dir = iter->second; |
| 386 // Remove directories recursively. | 346 // Remove directories recursively. |
| 387 dir->RemoveChildren(); | 347 dir->RemoveChildren(); |
| 388 if (directory_service_) | 348 if (directory_service_) |
| 389 directory_service_->RemoveEntryFromResourceMap(dir); | 349 directory_service_->RemoveEntryFromResourceMap(dir); |
| 390 } | 350 } |
| 391 STLDeleteValues(&child_directories_); | 351 STLDeleteValues(&child_directories_); |
| 392 child_directories_.clear(); | 352 child_directories_.clear(); |
| 393 } | 353 } |
| 394 | 354 |
| 395 // ResourceMetadataDB implementation. | |
| 396 | |
| 397 // Params for GDatadirectoryServiceDB::Create. | |
| 398 struct CreateDBParams { | |
| 399 CreateDBParams(const FilePath& db_path, | |
| 400 base::SequencedTaskRunner* blocking_task_runner) | |
| 401 : db_path(db_path), | |
| 402 blocking_task_runner(blocking_task_runner) { | |
| 403 } | |
| 404 | |
| 405 FilePath db_path; | |
| 406 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner; | |
| 407 scoped_ptr<ResourceMetadataDB> db; | |
| 408 GDataDirectoryService::SerializedMap serialized_resources; | |
| 409 }; | |
| 410 | |
| 411 // Wrapper for level db. All methods must be called on blocking thread. | |
| 412 class ResourceMetadataDB { | |
| 413 public: | |
| 414 ResourceMetadataDB(const FilePath& db_path, | |
| 415 base::SequencedTaskRunner* blocking_task_runner); | |
| 416 | |
| 417 // Initializes the database. | |
| 418 void Init(); | |
| 419 | |
| 420 // Reads the database into |serialized_resources|. | |
| 421 void Read(GDataDirectoryService::SerializedMap* serialized_resources); | |
| 422 | |
| 423 // Saves |serialized_resources| to the database. | |
| 424 void Save(const GDataDirectoryService::SerializedMap& serialized_resources); | |
| 425 | |
| 426 private: | |
| 427 // Clears the database. | |
| 428 void Clear(); | |
| 429 | |
| 430 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; | |
| 431 scoped_ptr<leveldb::DB> level_db_; | |
| 432 FilePath db_path_; | |
| 433 }; | |
| 434 | |
| 435 ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path, | |
| 436 base::SequencedTaskRunner* blocking_task_runner) | |
| 437 : blocking_task_runner_(blocking_task_runner), | |
| 438 db_path_(db_path) { | |
| 439 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 440 } | |
| 441 | |
| 442 // Creates, initializes and reads from the database. | |
| 443 // This must be defined after ResourceMetadataDB and CreateDBParams. | |
| 444 static void CreateResourceMetadataDBOnBlockingPool( | |
| 445 CreateDBParams* params) { | |
| 446 DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread()); | |
| 447 DCHECK(!params->db_path.empty()); | |
| 448 | |
| 449 params->db.reset(new ResourceMetadataDB(params->db_path, | |
| 450 params->blocking_task_runner)); | |
| 451 params->db->Init(); | |
| 452 params->db->Read(¶ms->serialized_resources); | |
| 453 } | |
| 454 | |
| 455 void ResourceMetadataDB::Init() { | |
| 456 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 457 DCHECK(!db_path_.empty()); | |
| 458 | |
| 459 DVLOG(1) << "Init " << db_path_.value(); | |
| 460 | |
| 461 leveldb::DB* level_db = NULL; | |
| 462 leveldb::Options options; | |
| 463 options.create_if_missing = true; | |
| 464 leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(), | |
| 465 &level_db); | |
| 466 DCHECK(level_db); | |
| 467 DCHECK(db_status.ok()); | |
| 468 level_db_.reset(level_db); | |
| 469 } | |
| 470 | |
| 471 void ResourceMetadataDB::Read( | |
| 472 GDataDirectoryService::SerializedMap* serialized_resources) { | |
| 473 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 474 DCHECK(serialized_resources); | |
| 475 DVLOG(1) << "Read " << db_path_.value(); | |
| 476 | |
| 477 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator( | |
| 478 leveldb::ReadOptions())); | |
| 479 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { | |
| 480 DVLOG(1) << "Read, resource " << iter->key().ToString(); | |
| 481 serialized_resources->insert(std::make_pair(iter->key().ToString(), | |
| 482 iter->value().ToString())); | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 void ResourceMetadataDB::Save( | |
| 487 const GDataDirectoryService::SerializedMap& serialized_resources) { | |
| 488 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 489 | |
| 490 Clear(); | |
| 491 for (GDataDirectoryService::SerializedMap::const_iterator iter = | |
| 492 serialized_resources.begin(); | |
| 493 iter != serialized_resources.end(); ++iter) { | |
| 494 DVLOG(1) << "Saving resource " << iter->first << " to db"; | |
| 495 leveldb::Status status = level_db_->Put(leveldb::WriteOptions(), | |
| 496 leveldb::Slice(iter->first), | |
| 497 leveldb::Slice(iter->second)); | |
| 498 if (!status.ok()) { | |
| 499 LOG(ERROR) << "leveldb Put failed of " << iter->first | |
| 500 << ", with " << status.ToString(); | |
| 501 NOTREACHED(); | |
| 502 } | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 void ResourceMetadataDB::Clear() { | |
| 507 level_db_.reset(); | |
| 508 leveldb::DestroyDB(db_path_.value(), leveldb::Options()); | |
| 509 Init(); | |
| 510 } | |
| 511 | |
| 512 // GDataDirectoryService class implementation. | |
| 513 | |
| 514 GDataDirectoryService::GDataDirectoryService() | |
| 515 : blocking_task_runner_(NULL), | |
| 516 serialized_size_(0), | |
| 517 largest_changestamp_(0), | |
| 518 origin_(UNINITIALIZED), | |
| 519 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 520 root_.reset(CreateGDataDirectory()); | |
| 521 if (!util::IsDriveV2ApiEnabled()) | |
| 522 InitializeRootEntry(kGDataRootDirectoryResourceId); | |
| 523 } | |
| 524 | |
| 525 GDataDirectoryService::~GDataDirectoryService() { | |
| 526 ClearRoot(); | |
| 527 | |
| 528 // Ensure db is closed on the blocking pool. | |
| 529 if (blocking_task_runner_ && directory_service_db_.get()) | |
| 530 blocking_task_runner_->DeleteSoon(FROM_HERE, | |
| 531 directory_service_db_.release()); | |
| 532 } | |
| 533 | |
| 534 GDataEntry* GDataDirectoryService::FromDocumentEntry(DocumentEntry* doc) { | |
| 535 DCHECK(doc); | |
| 536 GDataEntry* entry = NULL; | |
| 537 if (doc->is_folder()) | |
| 538 entry = CreateGDataDirectory(); | |
| 539 else if (doc->is_hosted_document() || doc->is_file()) | |
| 540 entry = CreateGDataFile(); | |
| 541 | |
| 542 if (entry) | |
| 543 entry->InitFromDocumentEntry(doc); | |
| 544 return entry; | |
| 545 } | |
| 546 | |
| 547 GDataFile* GDataDirectoryService::CreateGDataFile() { | |
| 548 return new GDataFile(this); | |
| 549 } | |
| 550 | |
| 551 GDataDirectory* GDataDirectoryService::CreateGDataDirectory() { | |
| 552 return new GDataDirectory(this); | |
| 553 } | |
| 554 | |
| 555 void GDataDirectoryService::InitializeRootEntry(const std::string& root_id) { | |
| 556 root_.reset(CreateGDataDirectory()); | |
| 557 root_->set_title(kGDataRootDirectory); | |
| 558 root_->SetBaseNameFromTitle(); | |
| 559 root_->set_resource_id(root_id); | |
| 560 AddEntryToResourceMap(root_.get()); | |
| 561 } | |
| 562 | |
| 563 void GDataDirectoryService::ClearRoot() { | |
| 564 // Note that children have a reference to root_, | |
| 565 // so we need to delete them here. | |
| 566 root_->RemoveChildren(); | |
| 567 RemoveEntryFromResourceMap(root_.get()); | |
| 568 DCHECK(resource_map_.empty()); | |
| 569 resource_map_.clear(); | |
| 570 root_.reset(); | |
| 571 } | |
| 572 | |
| 573 void GDataDirectoryService::AddEntryToDirectory( | |
| 574 GDataDirectory* directory, | |
| 575 GDataEntry* new_entry, | |
| 576 const FileMoveCallback& callback) { | |
| 577 DCHECK(directory); | |
| 578 DCHECK(new_entry); | |
| 579 DCHECK(!callback.is_null()); | |
| 580 | |
| 581 directory->AddEntry(new_entry); | |
| 582 DVLOG(1) << "AddEntryToDirectory " << new_entry->GetFilePath().value(); | |
| 583 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
| 584 base::Bind(callback, GDATA_FILE_OK, new_entry->GetFilePath())); | |
| 585 } | |
| 586 | |
| 587 void GDataDirectoryService::MoveEntryToDirectory( | |
| 588 const FilePath& directory_path, | |
| 589 GDataEntry* entry, | |
| 590 const FileMoveCallback& callback) { | |
| 591 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 592 DCHECK(entry); | |
| 593 DCHECK(!callback.is_null()); | |
| 594 | |
| 595 if (entry->parent()) | |
| 596 entry->parent()->RemoveChild(entry); | |
| 597 | |
| 598 GDataEntry* destination = FindEntryByPathSync(directory_path); | |
| 599 FilePath moved_file_path; | |
| 600 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 601 if (!destination) { | |
| 602 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 603 } else if (!destination->AsGDataDirectory()) { | |
| 604 error = GDATA_FILE_ERROR_NOT_A_DIRECTORY; | |
| 605 } else { | |
| 606 destination->AsGDataDirectory()->AddEntry(entry); | |
| 607 moved_file_path = entry->GetFilePath(); | |
| 608 error = GDATA_FILE_OK; | |
| 609 } | |
| 610 DVLOG(1) << "MoveEntryToDirectory " << moved_file_path.value(); | |
| 611 base::MessageLoopProxy::current()->PostTask( | |
| 612 FROM_HERE, base::Bind(callback, error, moved_file_path)); | |
| 613 } | |
| 614 | |
| 615 void GDataDirectoryService::RemoveEntryFromParent( | |
| 616 GDataEntry* entry, | |
| 617 const FileMoveCallback& callback) { | |
| 618 GDataDirectory* parent = entry->parent(); | |
| 619 DCHECK(parent); | |
| 620 DCHECK(!callback.is_null()); | |
| 621 DVLOG(1) << "RemoveEntryFromParent " << entry->GetFilePath().value(); | |
| 622 | |
| 623 parent->RemoveEntry(entry); | |
| 624 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
| 625 base::Bind(callback, GDATA_FILE_OK, parent->GetFilePath())); | |
| 626 } | |
| 627 | |
| 628 void GDataDirectoryService::AddEntryToResourceMap(GDataEntry* entry) { | |
| 629 // GDataFileSystem has already locked. | |
| 630 DVLOG(1) << "AddEntryToResourceMap " << entry->resource_id(); | |
| 631 resource_map_.insert(std::make_pair(entry->resource_id(), entry)); | |
| 632 } | |
| 633 | |
| 634 void GDataDirectoryService::RemoveEntryFromResourceMap(GDataEntry* entry) { | |
| 635 // GDataFileSystem has already locked. | |
| 636 resource_map_.erase(entry->resource_id()); | |
| 637 } | |
| 638 | |
| 639 GDataEntry* GDataDirectoryService::FindEntryByPathSync( | |
| 640 const FilePath& file_path) { | |
| 641 if (file_path == root_->GetFilePath()) | |
| 642 return root_.get(); | |
| 643 | |
| 644 std::vector<FilePath::StringType> components; | |
| 645 file_path.GetComponents(&components); | |
| 646 GDataDirectory* current_dir = root_.get(); | |
| 647 | |
| 648 for (size_t i = 1; i < components.size() && current_dir; ++i) { | |
| 649 GDataEntry* entry = current_dir->FindChild(components[i]); | |
| 650 if (!entry) | |
| 651 return NULL; | |
| 652 | |
| 653 if (i == components.size() - 1) // Last component. | |
| 654 return entry; | |
| 655 else | |
| 656 current_dir = entry->AsGDataDirectory(); | |
| 657 } | |
| 658 return NULL; | |
| 659 } | |
| 660 | |
| 661 void GDataDirectoryService::FindEntryByPathAndRunSync( | |
| 662 const FilePath& search_file_path, | |
| 663 const FindEntryCallback& callback) { | |
| 664 GDataEntry* entry = FindEntryByPathSync(search_file_path); | |
| 665 callback.Run(entry ? GDATA_FILE_OK : GDATA_FILE_ERROR_NOT_FOUND, entry); | |
| 666 } | |
| 667 | |
| 668 GDataEntry* GDataDirectoryService::GetEntryByResourceId( | |
| 669 const std::string& resource) { | |
| 670 // GDataFileSystem has already locked. | |
| 671 ResourceMap::const_iterator iter = resource_map_.find(resource); | |
| 672 return iter == resource_map_.end() ? NULL : iter->second; | |
| 673 } | |
| 674 | |
| 675 void GDataDirectoryService::GetEntryByResourceIdAsync( | |
| 676 const std::string& resource_id, | |
| 677 const GetEntryByResourceIdCallback& callback) { | |
| 678 GDataEntry* entry = GetEntryByResourceId(resource_id); | |
| 679 callback.Run(entry); | |
| 680 } | |
| 681 | |
| 682 void GDataDirectoryService::GetEntryInfoByPath( | |
| 683 const FilePath& path, | |
| 684 const GetEntryInfoCallback& callback) { | |
| 685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 686 DCHECK(!callback.is_null()); | |
| 687 | |
| 688 scoped_ptr<GDataEntryProto> entry_proto; | |
| 689 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 690 | |
| 691 GDataEntry* entry = FindEntryByPathSync(path); | |
| 692 if (entry) { | |
| 693 entry_proto.reset(new GDataEntryProto); | |
| 694 entry->ToProtoFull(entry_proto.get()); | |
| 695 error = GDATA_FILE_OK; | |
| 696 } else { | |
| 697 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 698 } | |
| 699 | |
| 700 base::MessageLoopProxy::current()->PostTask( | |
| 701 FROM_HERE, | |
| 702 base::Bind(callback, error, base::Passed(&entry_proto))); | |
| 703 } | |
| 704 | |
| 705 void GDataDirectoryService::ReadDirectoryByPath( | |
| 706 const FilePath& path, | |
| 707 const ReadDirectoryCallback& callback) { | |
| 708 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 709 DCHECK(!callback.is_null()); | |
| 710 | |
| 711 scoped_ptr<GDataEntryProtoVector> entries; | |
| 712 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 713 | |
| 714 GDataEntry* entry = FindEntryByPathSync(path); | |
| 715 if (entry && entry->AsGDataDirectory()) { | |
| 716 entries = entry->AsGDataDirectory()->ToProtoVector(); | |
| 717 error = GDATA_FILE_OK; | |
| 718 } else if (entry && !entry->AsGDataDirectory()) { | |
| 719 error = GDATA_FILE_ERROR_NOT_A_DIRECTORY; | |
| 720 } else { | |
| 721 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 722 } | |
| 723 | |
| 724 base::MessageLoopProxy::current()->PostTask( | |
| 725 FROM_HERE, | |
| 726 base::Bind(callback, error, base::Passed(&entries))); | |
| 727 } | |
| 728 | |
| 729 void GDataDirectoryService::GetEntryInfoPairByPaths( | |
| 730 const FilePath& first_path, | |
| 731 const FilePath& second_path, | |
| 732 const GetEntryInfoPairCallback& callback) { | |
| 733 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 734 DCHECK(!callback.is_null()); | |
| 735 | |
| 736 // Get the first entry. | |
| 737 GetEntryInfoByPath( | |
| 738 first_path, | |
| 739 base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst, | |
| 740 weak_ptr_factory_.GetWeakPtr(), | |
| 741 first_path, | |
| 742 second_path, | |
| 743 callback)); | |
| 744 } | |
| 745 | |
| 746 void GDataDirectoryService::RefreshFile(scoped_ptr<GDataFile> fresh_file) { | |
| 747 DCHECK(fresh_file.get()); | |
| 748 | |
| 749 // Need to get a reference here because Passed() could get evaluated first. | |
| 750 const std::string& resource_id = fresh_file->resource_id(); | |
| 751 GetEntryByResourceIdAsync( | |
| 752 resource_id, | |
| 753 base::Bind(&GDataDirectoryService::RefreshFileInternal, | |
| 754 base::Passed(&fresh_file))); | |
| 755 } | |
| 756 | |
| 757 // static | |
| 758 void GDataDirectoryService::RefreshFileInternal( | |
| 759 scoped_ptr<GDataFile> fresh_file, | |
| 760 GDataEntry* old_entry) { | |
| 761 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL; | |
| 762 if (entry_parent) { | |
| 763 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id()); | |
| 764 DCHECK(old_entry->AsGDataFile()); | |
| 765 | |
| 766 entry_parent->RemoveEntry(old_entry); | |
| 767 entry_parent->AddEntry(fresh_file.release()); | |
| 768 } | |
| 769 } | |
| 770 | |
| 771 void GDataDirectoryService::RefreshDirectory( | |
| 772 const std::string& directory_resource_id, | |
| 773 const ResourceMap& file_map, | |
| 774 const FileMoveCallback& callback) { | |
| 775 DCHECK(!callback.is_null()); | |
| 776 GetEntryByResourceIdAsync( | |
| 777 directory_resource_id, | |
| 778 base::Bind(&GDataDirectoryService::RefreshDirectoryInternal, | |
| 779 file_map, | |
| 780 callback)); | |
| 781 } | |
| 782 | |
| 783 // static | |
| 784 void GDataDirectoryService::RefreshDirectoryInternal( | |
| 785 const ResourceMap& file_map, | |
| 786 const FileMoveCallback& callback, | |
| 787 GDataEntry* directory_entry) { | |
| 788 DCHECK(!callback.is_null()); | |
| 789 | |
| 790 if (!directory_entry) { | |
| 791 callback.Run(GDATA_FILE_ERROR_NOT_FOUND, FilePath()); | |
| 792 return; | |
| 793 } | |
| 794 | |
| 795 GDataDirectory* directory = directory_entry->AsGDataDirectory(); | |
| 796 if (!directory) { | |
| 797 callback.Run(GDATA_FILE_ERROR_NOT_A_DIRECTORY, FilePath()); | |
| 798 return; | |
| 799 } | |
| 800 | |
| 801 directory->RemoveChildFiles(); | |
| 802 // Add files from file_map. | |
| 803 for (ResourceMap::const_iterator it = file_map.begin(); | |
| 804 it != file_map.end(); ++it) { | |
| 805 scoped_ptr<GDataEntry> entry(it->second); | |
| 806 // Skip if it's not a file (i.e. directory). | |
| 807 if (!entry->AsGDataFile()) | |
| 808 continue; | |
| 809 directory->AddEntry(entry.release()); | |
| 810 } | |
| 811 | |
| 812 callback.Run(GDATA_FILE_OK, directory->GetFilePath()); | |
| 813 } | |
| 814 | |
| 815 void GDataDirectoryService::InitFromDB( | |
| 816 const FilePath& db_path, | |
| 817 base::SequencedTaskRunner* blocking_task_runner, | |
| 818 const FileOperationCallback& callback) { | |
| 819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 820 DCHECK(!db_path.empty()); | |
| 821 DCHECK(blocking_task_runner); | |
| 822 | |
| 823 if (directory_service_db_.get()) { | |
| 824 if (!callback.is_null()) | |
| 825 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 826 return; | |
| 827 } | |
| 828 | |
| 829 blocking_task_runner_ = blocking_task_runner; | |
| 830 | |
| 831 DVLOG(1) << "InitFromDB " << db_path.value(); | |
| 832 | |
| 833 CreateDBParams* create_params = | |
| 834 new CreateDBParams(db_path, blocking_task_runner); | |
| 835 blocking_task_runner_->PostTaskAndReply( | |
| 836 FROM_HERE, | |
| 837 base::Bind(&CreateResourceMetadataDBOnBlockingPool, | |
| 838 create_params), | |
| 839 base::Bind(&GDataDirectoryService::InitResourceMap, | |
| 840 weak_ptr_factory_.GetWeakPtr(), | |
| 841 base::Owned(create_params), | |
| 842 callback)); | |
| 843 } | |
| 844 | |
| 845 void GDataDirectoryService::InitResourceMap( | |
| 846 CreateDBParams* create_params, | |
| 847 const FileOperationCallback& callback) { | |
| 848 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 849 DCHECK(create_params); | |
| 850 DCHECK(!directory_service_db_.get()); | |
| 851 | |
| 852 SerializedMap* serialized_resources = &create_params->serialized_resources; | |
| 853 directory_service_db_ = create_params->db.Pass(); | |
| 854 if (serialized_resources->empty()) { | |
| 855 origin_ = INITIALIZING; | |
| 856 if (!callback.is_null()) | |
| 857 callback.Run(GDATA_FILE_ERROR_NOT_FOUND); | |
| 858 return; | |
| 859 } | |
| 860 | |
| 861 ClearRoot(); | |
| 862 | |
| 863 // Version check. | |
| 864 int32 version = 0; | |
| 865 SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion); | |
| 866 if (iter == serialized_resources->end() || | |
| 867 !base::StringToInt(iter->second, &version) || | |
| 868 version != kProtoVersion) { | |
| 869 if (!callback.is_null()) | |
| 870 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 871 return; | |
| 872 } | |
| 873 serialized_resources->erase(iter); | |
| 874 | |
| 875 // Get the largest changestamp. | |
| 876 iter = serialized_resources->find(kDBKeyLargestChangestamp); | |
| 877 if (iter == serialized_resources->end() || | |
| 878 !base::StringToInt64(iter->second, &largest_changestamp_)) { | |
| 879 NOTREACHED() << "Could not find/parse largest_changestamp"; | |
| 880 if (!callback.is_null()) | |
| 881 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 882 return; | |
| 883 } else { | |
| 884 DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_; | |
| 885 serialized_resources->erase(iter); | |
| 886 } | |
| 887 | |
| 888 ResourceMap resource_map; | |
| 889 for (SerializedMap::const_iterator iter = serialized_resources->begin(); | |
| 890 iter != serialized_resources->end(); ++iter) { | |
| 891 if (iter->first.find(kDBKeyResourceIdPrefix) != 0) { | |
| 892 NOTREACHED() << "Incorrect prefix for db key " << iter->first; | |
| 893 continue; | |
| 894 } | |
| 895 | |
| 896 const std::string resource_id = | |
| 897 iter->first.substr(strlen(kDBKeyResourceIdPrefix)); | |
| 898 scoped_ptr<GDataEntry> entry = FromProtoString(iter->second); | |
| 899 if (entry.get()) { | |
| 900 DVLOG(1) << "Inserting resource " << resource_id | |
| 901 << " into resource_map"; | |
| 902 resource_map.insert(std::make_pair(resource_id, entry.release())); | |
| 903 } else { | |
| 904 NOTREACHED() << "Failed to parse GDataEntry for resource " << resource_id; | |
| 905 } | |
| 906 } | |
| 907 | |
| 908 // Fix up parent-child relations. | |
| 909 for (ResourceMap::iterator iter = resource_map.begin(); | |
| 910 iter != resource_map.end(); ++iter) { | |
| 911 GDataEntry* entry = iter->second; | |
| 912 ResourceMap::iterator parent_it = | |
| 913 resource_map.find(entry->parent_resource_id()); | |
| 914 if (parent_it != resource_map.end()) { | |
| 915 GDataDirectory* parent = parent_it->second->AsGDataDirectory(); | |
| 916 if (parent) { | |
| 917 DVLOG(1) << "Adding " << entry->resource_id() | |
| 918 << " as a child of " << parent->resource_id(); | |
| 919 parent->AddEntry(entry); | |
| 920 } else { | |
| 921 NOTREACHED() << "Parent is not a directory " << parent->resource_id(); | |
| 922 } | |
| 923 } else if (entry->resource_id() == kGDataRootDirectoryResourceId) { | |
| 924 root_.reset(entry->AsGDataDirectory()); | |
| 925 DCHECK(root_.get()); | |
| 926 AddEntryToResourceMap(root_.get()); | |
| 927 } else { | |
| 928 NOTREACHED() << "Missing parent id " << entry->parent_resource_id() | |
| 929 << " for resource " << entry->resource_id(); | |
| 930 } | |
| 931 } | |
| 932 | |
| 933 DCHECK(root_.get()); | |
| 934 DCHECK_EQ(resource_map.size(), resource_map_.size()); | |
| 935 DCHECK_EQ(resource_map.size(), serialized_resources->size()); | |
| 936 | |
| 937 origin_ = FROM_CACHE; | |
| 938 | |
| 939 if (!callback.is_null()) | |
| 940 callback.Run(GDATA_FILE_OK); | |
| 941 } | |
| 942 | |
| 943 void GDataDirectoryService::SaveToDB() { | |
| 944 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 945 | |
| 946 if (!blocking_task_runner_ || !directory_service_db_.get()) { | |
| 947 NOTREACHED(); | |
| 948 return; | |
| 949 } | |
| 950 | |
| 951 size_t serialized_size = 0; | |
| 952 SerializedMap serialized_resources; | |
| 953 for (ResourceMap::const_iterator iter = resource_map_.begin(); | |
| 954 iter != resource_map_.end(); ++iter) { | |
| 955 GDataEntryProto proto; | |
| 956 iter->second->ToProtoFull(&proto); | |
| 957 std::string serialized_string; | |
| 958 const bool ok = proto.SerializeToString(&serialized_string); | |
| 959 DCHECK(ok); | |
| 960 if (ok) { | |
| 961 serialized_resources.insert( | |
| 962 std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first, | |
| 963 serialized_string)); | |
| 964 serialized_size += serialized_string.size(); | |
| 965 } | |
| 966 } | |
| 967 | |
| 968 serialized_resources.insert(std::make_pair(kDBKeyVersion, | |
| 969 base::IntToString(kProtoVersion))); | |
| 970 serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp, | |
| 971 base::IntToString(largest_changestamp_))); | |
| 972 set_last_serialized(base::Time::Now()); | |
| 973 set_serialized_size(serialized_size); | |
| 974 | |
| 975 blocking_task_runner_->PostTask( | |
| 976 FROM_HERE, | |
| 977 base::Bind(&ResourceMetadataDB::Save, | |
| 978 base::Unretained(directory_service_db_.get()), | |
| 979 serialized_resources)); | |
| 980 } | |
| 981 | |
| 982 // Convert to/from proto. | 355 // Convert to/from proto. |
| 983 | 356 |
| 984 // static | 357 // static |
| 985 void GDataEntry::ConvertProtoToPlatformFileInfo( | 358 void GDataEntry::ConvertProtoToPlatformFileInfo( |
| 986 const PlatformFileInfoProto& proto, | 359 const PlatformFileInfoProto& proto, |
| 987 base::PlatformFileInfo* file_info) { | 360 base::PlatformFileInfo* file_info) { |
| 988 file_info->size = proto.size(); | 361 file_info->size = proto.size(); |
| 989 file_info->is_directory = proto.is_directory(); | 362 file_info->is_directory = proto.is_directory(); |
| 990 file_info->is_symbolic_link = proto.is_symbolic_link(); | 363 file_info->is_symbolic_link = proto.is_symbolic_link(); |
| 991 file_info->last_modified = base::Time::FromInternalValue( | 364 file_info->last_modified = base::Time::FromInternalValue( |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1163 const bool ok = entry_proto.SerializeToString(serialized_proto); | 536 const bool ok = entry_proto.SerializeToString(serialized_proto); |
| 1164 DCHECK(ok); | 537 DCHECK(ok); |
| 1165 } else if (dir) { | 538 } else if (dir) { |
| 1166 GDataDirectoryProto dir_proto; | 539 GDataDirectoryProto dir_proto; |
| 1167 dir->ToProto(&dir_proto); | 540 dir->ToProto(&dir_proto); |
| 1168 const bool ok = dir_proto.SerializeToString(serialized_proto); | 541 const bool ok = dir_proto.SerializeToString(serialized_proto); |
| 1169 DCHECK(ok); | 542 DCHECK(ok); |
| 1170 } | 543 } |
| 1171 } | 544 } |
| 1172 | 545 |
| 1173 void GDataDirectoryService::SerializeToString( | |
| 1174 std::string* serialized_proto) const { | |
| 1175 GDataRootDirectoryProto proto; | |
| 1176 root_->ToProto(proto.mutable_gdata_directory()); | |
| 1177 proto.set_largest_changestamp(largest_changestamp_); | |
| 1178 proto.set_version(kProtoVersion); | |
| 1179 | |
| 1180 const bool ok = proto.SerializeToString(serialized_proto); | |
| 1181 DCHECK(ok); | |
| 1182 } | |
| 1183 | |
| 1184 bool GDataDirectoryService::ParseFromString( | |
| 1185 const std::string& serialized_proto) { | |
| 1186 GDataRootDirectoryProto proto; | |
| 1187 if (!proto.ParseFromString(serialized_proto)) | |
| 1188 return false; | |
| 1189 | |
| 1190 if (proto.version() != kProtoVersion) { | |
| 1191 LOG(ERROR) << "Incompatible proto detected (incompatible version): " | |
| 1192 << proto.version(); | |
| 1193 return false; | |
| 1194 } | |
| 1195 | |
| 1196 if (!IsValidRootDirectoryProto(proto.gdata_directory())) | |
| 1197 return false; | |
| 1198 | |
| 1199 if (!root_->FromProto(proto.gdata_directory())) | |
| 1200 return false; | |
| 1201 | |
| 1202 origin_ = FROM_CACHE; | |
| 1203 largest_changestamp_ = proto.largest_changestamp(); | |
| 1204 | |
| 1205 return true; | |
| 1206 } | |
| 1207 | |
| 1208 scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString( | |
| 1209 const std::string& serialized_proto) { | |
| 1210 GDataEntryProto entry_proto; | |
| 1211 if (!entry_proto.ParseFromString(serialized_proto)) | |
| 1212 return scoped_ptr<GDataEntry>(); | |
| 1213 | |
| 1214 scoped_ptr<GDataEntry> entry; | |
| 1215 if (entry_proto.file_info().is_directory()) { | |
| 1216 entry.reset(CreateGDataDirectory()); | |
| 1217 // Call GDataEntry::FromProto instead of GDataDirectory::FromProto because | |
| 1218 // the proto does not include children. | |
| 1219 if (!entry->FromProto(entry_proto)) { | |
| 1220 NOTREACHED() << "FromProto (directory) failed"; | |
| 1221 entry.reset(); | |
| 1222 } | |
| 1223 } else { | |
| 1224 scoped_ptr<GDataFile> file(CreateGDataFile()); | |
| 1225 // Call GDataFile::FromProto. | |
| 1226 if (file->FromProto(entry_proto)) { | |
| 1227 entry.reset(file.release()); | |
| 1228 } else { | |
| 1229 NOTREACHED() << "FromProto (file) failed"; | |
| 1230 } | |
| 1231 } | |
| 1232 return entry.Pass(); | |
| 1233 } | |
| 1234 | |
| 1235 void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst( | |
| 1236 const FilePath& first_path, | |
| 1237 const FilePath& second_path, | |
| 1238 const GetEntryInfoPairCallback& callback, | |
| 1239 GDataFileError error, | |
| 1240 scoped_ptr<GDataEntryProto> entry_proto) { | |
| 1241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1242 DCHECK(!callback.is_null()); | |
| 1243 | |
| 1244 scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult); | |
| 1245 result->first.path = first_path; | |
| 1246 result->first.error = error; | |
| 1247 result->first.proto = entry_proto.Pass(); | |
| 1248 | |
| 1249 // If the first one is not found, don't continue. | |
| 1250 if (error != GDATA_FILE_OK) { | |
| 1251 callback.Run(result.Pass()); | |
| 1252 return; | |
| 1253 } | |
| 1254 | |
| 1255 // Get the second entry. | |
| 1256 GetEntryInfoByPath( | |
| 1257 second_path, | |
| 1258 base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond, | |
| 1259 weak_ptr_factory_.GetWeakPtr(), | |
| 1260 second_path, | |
| 1261 callback, | |
| 1262 base::Passed(&result))); | |
| 1263 } | |
| 1264 | |
| 1265 void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond( | |
| 1266 const FilePath& second_path, | |
| 1267 const GetEntryInfoPairCallback& callback, | |
| 1268 scoped_ptr<EntryInfoPairResult> result, | |
| 1269 GDataFileError error, | |
| 1270 scoped_ptr<GDataEntryProto> entry_proto) { | |
| 1271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1272 DCHECK(!callback.is_null()); | |
| 1273 DCHECK(result.get()); | |
| 1274 | |
| 1275 result->second.path = second_path; | |
| 1276 result->second.error = error; | |
| 1277 result->second.proto = entry_proto.Pass(); | |
| 1278 | |
| 1279 callback.Run(result.Pass()); | |
| 1280 } | |
| 1281 | |
| 1282 } // namespace gdata | 546 } // namespace gdata |
| OLD | NEW |