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> | |
8 | |
9 #include "base/message_loop_proxy.h" | 7 #include "base/message_loop_proxy.h" |
10 #include "base/platform_file.h" | 8 #include "base/platform_file.h" |
11 #include "base/string_number_conversions.h" | 9 #include "base/string_number_conversions.h" |
12 #include "base/string_util.h" | 10 #include "base/string_util.h" |
13 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
14 #include "base/sequenced_task_runner.h" | 12 #include "base/sequenced_task_runner.h" |
15 #include "base/tracked_objects.h" | 13 #include "base/tracked_objects.h" |
16 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
17 #include "chrome/browser/chromeos/gdata/gdata.pb.h" | 15 #include "chrome/browser/chromeos/gdata/gdata.pb.h" |
| 16 #include "chrome/browser/chromeos/gdata/gdata_directory_service.h" |
18 #include "chrome/browser/chromeos/gdata/gdata_util.h" | 17 #include "chrome/browser/chromeos/gdata/gdata_util.h" |
19 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" | 18 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" |
20 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
21 #include "net/base/escape.h" | 20 #include "net/base/escape.h" |
22 | 21 |
23 using content::BrowserThread; | 22 using content::BrowserThread; |
24 | 23 |
25 namespace gdata { | 24 namespace gdata { |
26 namespace { | 25 namespace { |
27 | 26 |
28 const char kSlash[] = "/"; | 27 const char kSlash[] = "/"; |
29 const char kEscapedSlash[] = "\xE2\x88\x95"; | 28 const char kEscapedSlash[] = "\xE2\x88\x95"; |
30 | 29 |
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. | 30 // Extracts resource_id out of edit url. |
38 std::string ExtractResourceId(const GURL& url) { | 31 std::string ExtractResourceId(const GURL& url) { |
39 return net::UnescapeURLComponent(url.ExtractFileName(), | 32 return net::UnescapeURLComponent(url.ExtractFileName(), |
40 net::UnescapeRule::URL_SPECIAL_CHARS); | 33 net::UnescapeRule::URL_SPECIAL_CHARS); |
41 } | 34 } |
42 | 35 |
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 | 36 } // namespace |
67 | 37 |
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. | 38 // GDataEntry class. |
81 | 39 |
82 GDataEntry::GDataEntry(GDataDirectoryService* directory_service) | 40 GDataEntry::GDataEntry(GDataDirectoryService* directory_service) |
83 : parent_(NULL), | 41 : parent_(NULL), |
84 directory_service_(directory_service), | 42 directory_service_(directory_service), |
85 deleted_(false) { | 43 deleted_(false) { |
86 } | 44 } |
87 | 45 |
88 GDataEntry::~GDataEntry() { | 46 GDataEntry::~GDataEntry() { |
89 } | 47 } |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 GDataDirectory* dir = iter->second; | 343 GDataDirectory* dir = iter->second; |
386 // Remove directories recursively. | 344 // Remove directories recursively. |
387 dir->RemoveChildren(); | 345 dir->RemoveChildren(); |
388 if (directory_service_) | 346 if (directory_service_) |
389 directory_service_->RemoveEntryFromResourceMap(dir); | 347 directory_service_->RemoveEntryFromResourceMap(dir); |
390 } | 348 } |
391 STLDeleteValues(&child_directories_); | 349 STLDeleteValues(&child_directories_); |
392 child_directories_.clear(); | 350 child_directories_.clear(); |
393 } | 351 } |
394 | 352 |
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. | 353 // Convert to/from proto. |
983 | 354 |
984 // static | 355 // static |
985 void GDataEntry::ConvertProtoToPlatformFileInfo( | 356 void GDataEntry::ConvertProtoToPlatformFileInfo( |
986 const PlatformFileInfoProto& proto, | 357 const PlatformFileInfoProto& proto, |
987 base::PlatformFileInfo* file_info) { | 358 base::PlatformFileInfo* file_info) { |
988 file_info->size = proto.size(); | 359 file_info->size = proto.size(); |
989 file_info->is_directory = proto.is_directory(); | 360 file_info->is_directory = proto.is_directory(); |
990 file_info->is_symbolic_link = proto.is_symbolic_link(); | 361 file_info->is_symbolic_link = proto.is_symbolic_link(); |
991 file_info->last_modified = base::Time::FromInternalValue( | 362 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); | 534 const bool ok = entry_proto.SerializeToString(serialized_proto); |
1164 DCHECK(ok); | 535 DCHECK(ok); |
1165 } else if (dir) { | 536 } else if (dir) { |
1166 GDataDirectoryProto dir_proto; | 537 GDataDirectoryProto dir_proto; |
1167 dir->ToProto(&dir_proto); | 538 dir->ToProto(&dir_proto); |
1168 const bool ok = dir_proto.SerializeToString(serialized_proto); | 539 const bool ok = dir_proto.SerializeToString(serialized_proto); |
1169 DCHECK(ok); | 540 DCHECK(ok); |
1170 } | 541 } |
1171 } | 542 } |
1172 | 543 |
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 | 544 } // namespace gdata |
OLD | NEW |