Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(272)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_files.cc

Issue 10800092: Database support for GDataDirectoryService. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: rebase Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 <vector> 8 #include <vector>
8 9
9 #include "base/message_loop_proxy.h" 10 #include "base/message_loop_proxy.h"
10 #include "base/platform_file.h" 11 #include "base/platform_file.h"
12 #include "base/string_number_conversions.h"
11 #include "base/string_util.h" 13 #include "base/string_util.h"
12 #include "base/stringprintf.h" 14 #include "base/stringprintf.h"
15 #include "base/sequenced_task_runner.h"
13 #include "base/tracked_objects.h" 16 #include "base/tracked_objects.h"
14 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/chromeos/gdata/gdata.pb.h" 18 #include "chrome/browser/chromeos/gdata/gdata.pb.h"
16 #include "chrome/browser/chromeos/gdata/gdata_util.h" 19 #include "chrome/browser/chromeos/gdata/gdata_util.h"
17 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" 20 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h"
21 #include "content/public/browser/browser_thread.h"
18 #include "net/base/escape.h" 22 #include "net/base/escape.h"
19 23
24 using content::BrowserThread;
25
20 namespace gdata { 26 namespace gdata {
21 namespace { 27 namespace {
22 28
23 const char kSlash[] = "/"; 29 const char kSlash[] = "/";
24 const char kEscapedSlash[] = "\xE2\x88\x95"; 30 const char kEscapedSlash[] = "\xE2\x88\x95";
25 31
32 // m: prefix for filesystem metadata db keys, version and largest_changestamp.
33 // r: prefix for resource id db keys.
34 const char kDBKeyLargestChangestamp[] = "m:largest_changestamp";
35 const char kDBKeyVersion[] = "m:version";
36 const char kDBKeyResourceIdPrefix[] = "r:";
37
26 // Extracts resource_id out of edit url. 38 // Extracts resource_id out of edit url.
27 std::string ExtractResourceId(const GURL& url) { 39 std::string ExtractResourceId(const GURL& url) {
28 return net::UnescapeURLComponent(url.ExtractFileName(), 40 return net::UnescapeURLComponent(url.ExtractFileName(),
29 net::UnescapeRule::URL_SPECIAL_CHARS); 41 net::UnescapeRule::URL_SPECIAL_CHARS);
30 } 42 }
31 43
32 // Returns true if |proto| is a valid proto as the root directory. 44 // Returns true if |proto| is a valid proto as the root directory.
33 // Used to reject incompatible proto. 45 // Used to reject incompatible proto.
34 bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) { 46 bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) {
35 const GDataEntryProto& entry_proto = proto.gdata_entry(); 47 const GDataEntryProto& entry_proto = proto.gdata_entry();
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 GDataDirectory* dir = iter->second; 428 GDataDirectory* dir = iter->second;
417 // Remove directories recursively. 429 // Remove directories recursively.
418 dir->RemoveChildren(); 430 dir->RemoveChildren();
419 if (directory_service_) 431 if (directory_service_)
420 directory_service_->RemoveEntryFromResourceMap(dir); 432 directory_service_->RemoveEntryFromResourceMap(dir);
421 } 433 }
422 STLDeleteValues(&child_directories_); 434 STLDeleteValues(&child_directories_);
423 child_directories_.clear(); 435 child_directories_.clear();
424 } 436 }
425 437
438 // ResourceMetadataDB implementation.
439
440 // Params for GDatadirectoryServiceDB::Create.
441 struct CreateDBParams {
442 CreateDBParams(const FilePath& db_path,
443 base::SequencedTaskRunner* blocking_task_runner)
444 : db_path(db_path),
445 blocking_task_runner(blocking_task_runner) {
446 }
447
448 FilePath db_path;
449 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
450 scoped_ptr<ResourceMetadataDB> db;
451 GDataDirectoryService::SerializedMap serialized_resources;
452 };
453
454 // Wrapper for level db. All methods must be called on blocking thread.
455 class ResourceMetadataDB {
456 public:
457 ResourceMetadataDB(const FilePath& db_path,
458 base::SequencedTaskRunner* blocking_task_runner);
459
460 // Initializes the database.
461 void Init();
462
463 // Reads the database into |serialized_resources|.
464 void Read(GDataDirectoryService::SerializedMap* serialized_resources);
465
466 // Saves |serialized_resources| to the database.
467 void Save(const GDataDirectoryService::SerializedMap& serialized_resources);
468
469 private:
470 // Clears the database.
471 void Clear();
472
473 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
474 scoped_ptr<leveldb::DB> level_db_;
475 FilePath db_path_;
476 };
477
478 ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path,
479 base::SequencedTaskRunner* blocking_task_runner)
480 : blocking_task_runner_(blocking_task_runner),
481 db_path_(db_path) {
482 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
483 }
484
485 // Creates, initializes and reads from the database.
486 // This must be defined after ResourceMetadataDB and CreateDBParams.
487 static void CreateResourceMetadataDBOnBlockingPool(
488 CreateDBParams* params) {
489 DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
490 DCHECK(!params->db_path.empty());
491
492 params->db.reset(new ResourceMetadataDB(params->db_path,
493 params->blocking_task_runner));
494 params->db->Init();
495 params->db->Read(&params->serialized_resources);
496 }
497
498 void ResourceMetadataDB::Init() {
499 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
500 DCHECK(!db_path_.empty());
501
502 DVLOG(1) << "Init " << db_path_.value();
503
504 leveldb::DB* level_db = NULL;
505 leveldb::Options options;
506 options.create_if_missing = true;
507 leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(),
508 &level_db);
509 DCHECK(level_db);
510 DCHECK(db_status.ok());
511 level_db_.reset(level_db);
512 }
513
514 void ResourceMetadataDB::Read(
515 GDataDirectoryService::SerializedMap* serialized_resources) {
516 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
517 DCHECK(serialized_resources);
518 DVLOG(1) << "Read " << db_path_.value();
519
520 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
521 leveldb::ReadOptions()));
522 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
523 DVLOG(1) << "Read, resource " << iter->key().ToString();
524 serialized_resources->insert(std::make_pair(iter->key().ToString(),
525 iter->value().ToString()));
526 }
527 }
528
529 void ResourceMetadataDB::Save(
530 const GDataDirectoryService::SerializedMap& serialized_resources) {
531 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
532
533 Clear();
534 for (GDataDirectoryService::SerializedMap::const_iterator iter =
535 serialized_resources.begin();
536 iter != serialized_resources.end(); ++iter) {
537 DVLOG(1) << "Saving resource " << iter->first << " to db";
538 leveldb::Status status = level_db_->Put(leveldb::WriteOptions(),
539 leveldb::Slice(iter->first),
540 leveldb::Slice(iter->second));
541 if (!status.ok()) {
542 LOG(ERROR) << "leveldb Put failed of " << iter->first
543 << ", with " << status.ToString();
544 NOTREACHED();
545 }
546 }
547 }
548
549 void ResourceMetadataDB::Clear() {
550 level_db_.reset();
551 leveldb::DestroyDB(db_path_.value(), leveldb::Options());
552 Init();
553 }
554
426 // GDataDirectoryService class implementation. 555 // GDataDirectoryService class implementation.
427 556
428 GDataDirectoryService::GDataDirectoryService() 557 GDataDirectoryService::GDataDirectoryService()
429 : serialized_size_(0), 558 : blocking_task_runner_(NULL),
559 serialized_size_(0),
430 largest_changestamp_(0), 560 largest_changestamp_(0),
431 origin_(UNINITIALIZED) { 561 origin_(UNINITIALIZED),
562 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
432 root_.reset(new GDataDirectory(NULL, this)); 563 root_.reset(new GDataDirectory(NULL, this));
433 root_->set_title(kGDataRootDirectory); 564 root_->set_title(kGDataRootDirectory);
434 root_->SetBaseNameFromTitle(); 565 root_->SetBaseNameFromTitle();
435 root_->set_resource_id(kGDataRootDirectoryResourceId); 566 root_->set_resource_id(kGDataRootDirectoryResourceId);
436 AddEntryToResourceMap(root_.get()); 567 AddEntryToResourceMap(root_.get());
437 } 568 }
438 569
439 GDataDirectoryService::~GDataDirectoryService() { 570 GDataDirectoryService::~GDataDirectoryService() {
571 ClearRoot();
572
573 // Ensure db is closed on the blocking pool.
574 if (blocking_task_runner_ && directory_service_db_.get())
575 blocking_task_runner_->DeleteSoon(FROM_HERE,
576 directory_service_db_.release());
577 }
578
579 void GDataDirectoryService::ClearRoot() {
440 // Note that children have a reference to root_, 580 // Note that children have a reference to root_,
441 // so we need to delete them here. 581 // so we need to delete them here.
442 root_->RemoveChildren(); 582 root_->RemoveChildren();
443 RemoveEntryFromResourceMap(root_.get()); 583 RemoveEntryFromResourceMap(root_.get());
444 DCHECK(resource_map_.empty()); 584 DCHECK(resource_map_.empty());
445 resource_map_.clear(); 585 resource_map_.clear();
586 root_.reset();
446 } 587 }
447 588
448 void GDataDirectoryService::AddEntryToDirectory( 589 void GDataDirectoryService::AddEntryToDirectory(
449 const FilePath& directory_path, 590 const FilePath& directory_path,
450 GDataEntry* entry, 591 GDataEntry* entry,
451 const FileOperationCallback& callback) { 592 const FileOperationCallback& callback) {
452 GDataEntry* destination = FindEntryByPathSync(directory_path); 593 GDataEntry* destination = FindEntryByPathSync(directory_path);
453 GDataFileError error = GDATA_FILE_ERROR_FAILED; 594 GDataFileError error = GDATA_FILE_ERROR_FAILED;
454 if (!destination) { 595 if (!destination) {
455 error = GDATA_FILE_ERROR_NOT_FOUND; 596 error = GDATA_FILE_ERROR_NOT_FOUND;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL; 695 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL;
555 if (entry_parent) { 696 if (entry_parent) {
556 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id()); 697 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id());
557 DCHECK(old_entry->AsGDataFile()); 698 DCHECK(old_entry->AsGDataFile());
558 699
559 entry_parent->RemoveEntry(old_entry); 700 entry_parent->RemoveEntry(old_entry);
560 entry_parent->AddEntry(fresh_file.release()); 701 entry_parent->AddEntry(fresh_file.release());
561 } 702 }
562 } 703 }
563 704
705 void GDataDirectoryService::InitFromDB(
706 const FilePath& db_path,
707 base::SequencedTaskRunner* blocking_task_runner,
708 const FileOperationCallback& callback) {
709 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
710 DCHECK(!db_path.empty());
711 DCHECK(blocking_task_runner);
712
713 if (directory_service_db_.get()) {
714 if (!callback.is_null())
715 callback.Run(GDATA_FILE_ERROR_FAILED);
716 return;
717 }
718
719 blocking_task_runner_ = blocking_task_runner;
720
721 DVLOG(1) << "InitFromDB " << db_path.value();
722
723 CreateDBParams* create_params =
724 new CreateDBParams(db_path, blocking_task_runner);
725 blocking_task_runner_->PostTaskAndReply(
726 FROM_HERE,
727 base::Bind(&CreateResourceMetadataDBOnBlockingPool,
728 create_params),
729 base::Bind(&GDataDirectoryService::InitResourceMap,
730 weak_ptr_factory_.GetWeakPtr(),
731 base::Owned(create_params),
732 callback));
733 }
734
735 void GDataDirectoryService::InitResourceMap(
736 CreateDBParams* create_params,
737 const FileOperationCallback& callback) {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739 DCHECK(create_params);
740 DCHECK(!directory_service_db_.get());
741
742 SerializedMap* serialized_resources = &create_params->serialized_resources;
743 directory_service_db_ = create_params->db.Pass();
744 if (serialized_resources->empty()) {
745 origin_ = INITIALIZING;
746 if (!callback.is_null())
747 callback.Run(GDATA_FILE_ERROR_NOT_FOUND);
748 return;
749 }
750
751 ClearRoot();
752
753 // Version check.
754 int32 version = 0;
755 SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion);
756 if (iter == serialized_resources->end() ||
757 !base::StringToInt(iter->second, &version) ||
758 version != kProtoVersion) {
759 if (!callback.is_null())
760 callback.Run(GDATA_FILE_ERROR_FAILED);
761 return;
762 }
763 serialized_resources->erase(iter);
764
765 // Get the largest changestamp.
766 iter = serialized_resources->find(kDBKeyLargestChangestamp);
767 if (iter == serialized_resources->end() ||
768 !base::StringToInt(iter->second, &largest_changestamp_)) {
769 NOTREACHED() << "Could not find/parse largest_changestamp";
770 if (!callback.is_null())
771 callback.Run(GDATA_FILE_ERROR_FAILED);
772 return;
773 } else {
774 DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_;
775 serialized_resources->erase(iter);
776 }
777
778 ResourceMap resource_map;
779 for (SerializedMap::const_iterator iter = serialized_resources->begin();
780 iter != serialized_resources->end(); ++iter) {
781 if (iter->first.find(kDBKeyResourceIdPrefix) != 0) {
782 NOTREACHED() << "Incorrect prefix for db key " << iter->first;
783 continue;
784 }
785
786 const std::string resource_id =
787 iter->first.substr(strlen(kDBKeyResourceIdPrefix));
788 scoped_ptr<GDataEntry> entry = FromProtoString(iter->second);
789 if (entry.get()) {
790 DVLOG(1) << "Inserting resource " << resource_id
791 << " into resource_map";
792 resource_map.insert(std::make_pair(resource_id, entry.release()));
793 } else {
794 NOTREACHED() << "Failed to parse GDataEntry for resource " << resource_id;
795 }
796 }
797
798 // Fix up parent-child relations.
799 for (ResourceMap::iterator iter = resource_map.begin();
800 iter != resource_map.end(); ++iter) {
801 GDataEntry* entry = iter->second;
802 ResourceMap::iterator parent_it =
803 resource_map.find(entry->parent_resource_id());
804 if (parent_it != resource_map.end()) {
805 GDataDirectory* parent = parent_it->second->AsGDataDirectory();
806 if (parent) {
807 DVLOG(1) << "Adding " << entry->resource_id()
808 << " as a child of " << parent->resource_id();
809 parent->AddEntry(entry);
810 } else {
811 NOTREACHED() << "Parent is not a directory " << parent->resource_id();
812 }
813 } else if (entry->resource_id() == kGDataRootDirectoryResourceId) {
814 root_.reset(entry->AsGDataDirectory());
815 DCHECK(root_.get());
816 AddEntryToResourceMap(root_.get());
817 } else {
818 NOTREACHED() << "Missing parent id " << entry->parent_resource_id()
819 << " for resource " << entry->resource_id();
820 }
821 }
822
823 DCHECK(root_.get());
824 DCHECK_EQ(resource_map.size(), resource_map_.size());
825 DCHECK_EQ(resource_map.size(), serialized_resources->size());
826
827 origin_ = FROM_CACHE;
828
829 if (!callback.is_null())
830 callback.Run(GDATA_FILE_OK);
831 }
832
833 void GDataDirectoryService::SaveToDB() {
834 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
835
836 if (!blocking_task_runner_ || !directory_service_db_.get()) {
837 NOTREACHED();
838 return;
839 }
840
841 size_t serialized_size = 0;
842 SerializedMap serialized_resources;
843 for (ResourceMap::const_iterator iter = resource_map_.begin();
844 iter != resource_map_.end(); ++iter) {
845 GDataEntryProto proto;
846 iter->second->ToProtoFull(&proto);
847 std::string serialized_string;
848 const bool ok = proto.SerializeToString(&serialized_string);
849 DCHECK(ok);
850 if (ok) {
851 serialized_resources.insert(
852 std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first,
853 serialized_string));
854 serialized_size += serialized_string.size();
855 }
856 }
857
858 serialized_resources.insert(std::make_pair(kDBKeyVersion,
859 base::IntToString(kProtoVersion)));
860 serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp,
861 base::IntToString(largest_changestamp_)));
862 set_last_serialized(base::Time::Now());
863 set_serialized_size(serialized_size);
864
865 blocking_task_runner_->PostTask(
866 FROM_HERE,
867 base::Bind(&ResourceMetadataDB::Save,
868 base::Unretained(directory_service_db_.get()),
869 serialized_resources));
870 }
871
564 // Convert to/from proto. 872 // Convert to/from proto.
565 873
566 // static 874 // static
567 void GDataEntry::ConvertProtoToPlatformFileInfo( 875 void GDataEntry::ConvertProtoToPlatformFileInfo(
568 const PlatformFileInfoProto& proto, 876 const PlatformFileInfoProto& proto,
569 base::PlatformFileInfo* file_info) { 877 base::PlatformFileInfo* file_info) {
570 file_info->size = proto.size(); 878 file_info->size = proto.size();
571 file_info->is_directory = proto.is_directory(); 879 file_info->is_directory = proto.is_directory();
572 file_info->is_symbolic_link = proto.is_symbolic_link(); 880 file_info->is_symbolic_link = proto.is_symbolic_link();
573 file_info->last_modified = base::Time::FromInternalValue( 881 file_info->last_modified = base::Time::FromInternalValue(
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 1069
762 if (!root_->FromProto(proto.gdata_directory())) 1070 if (!root_->FromProto(proto.gdata_directory()))
763 return false; 1071 return false;
764 1072
765 origin_ = FROM_CACHE; 1073 origin_ = FROM_CACHE;
766 largest_changestamp_ = proto.largest_changestamp(); 1074 largest_changestamp_ = proto.largest_changestamp();
767 1075
768 return true; 1076 return true;
769 } 1077 }
770 1078
1079 scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString(
1080 const std::string& serialized_proto) {
1081 GDataEntryProto entry_proto;
1082 if (!entry_proto.ParseFromString(serialized_proto))
1083 return scoped_ptr<GDataEntry>();
1084
1085 scoped_ptr<GDataEntry> entry;
1086 if (entry_proto.file_info().is_directory()) {
1087 entry.reset(new GDataDirectory(NULL, this));
1088 // Call GDataEntry::FromProto instead of GDataDirectory::FromProto because
1089 // the proto does not include children.
1090 if (!entry->FromProto(entry_proto)) {
1091 NOTREACHED() << "FromProto (directory) failed";
1092 entry.reset();
1093 }
1094 } else {
1095 scoped_ptr<GDataFile> file(new GDataFile(NULL, this));
1096 // Call GDataFile::FromProto.
1097 if (file->FromProto(entry_proto)) {
1098 entry.reset(file.release());
1099 } else {
1100 NOTREACHED() << "FromProto (file) failed";
1101 }
1102 }
1103 return entry.Pass();
1104 }
1105
771 } // namespace gdata 1106 } // namespace gdata
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/gdata/gdata_files.h ('k') | chrome/browser/chromeos/gdata/gdata_files_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698