Chromium Code Reviews| Index: webkit/fileapi/syncable/local_file_change_tracker.cc |
| diff --git a/webkit/fileapi/syncable/local_file_change_tracker.cc b/webkit/fileapi/syncable/local_file_change_tracker.cc |
| index 81e61a094b956a2dabd3a8c07f9b5ba2d0f31939..942d3e5f251a726c3561817da9821a3a42b3abd8 100644 |
| --- a/webkit/fileapi/syncable/local_file_change_tracker.cc |
| +++ b/webkit/fileapi/syncable/local_file_change_tracker.cc |
| @@ -4,20 +4,70 @@ |
| #include "webkit/fileapi/syncable/local_file_change_tracker.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| #include "base/sequenced_task_runner.h" |
| +#include "third_party/leveldatabase/src/include/leveldb/db.h" |
| +#include "webkit/fileapi/file_system_util.h" |
| #include "webkit/fileapi/syncable/local_file_sync_status.h" |
| +namespace { |
| +const FilePath::CharType kDatabaseName[] = |
| + FILE_PATH_LITERAL("LocalFileChangeTracker"); |
| +const char kMark[] = "d"; |
| +} // namespace |
| + |
| namespace fileapi { |
| +// A database class that stores local file changes in a local database. This |
| +// object must be destructed on file_task_runner. |
| +class LocalFileChangeTracker::TrackerDB { |
| + public: |
| + explicit TrackerDB(const FilePath& profile_path); |
| + |
| + bool MarkDirty(const std::string& url); |
| + bool ClearDirty(const std::string& url); |
| + |
| + private: |
| + enum InitStatus { |
| + INIT_STATUS_OK = 0, |
| + INIT_STATUS_CORRUPTION, |
| + INIT_STATUS_IO_ERROR, |
| + INIT_STATUS_UNKNOWN_ERROR, |
| + INIT_STATUS_MAX, |
| + }; |
| + |
| + enum RecoveryOption { |
| + REPAIR_ON_CORRUPTION, |
| + FAIL_ON_CORRUPTION, |
| + }; |
| + |
| + bool Init(RecoveryOption recovery_option); |
| + bool Repair(const std::string& db_path); |
| + void HandleError(const tracked_objects::Location& from_here, |
| + const leveldb::Status& status); |
| + |
| + const FilePath profile_path_; |
| + scoped_ptr<leveldb::DB> db_; |
| + base::Time last_reported_time_; |
| + bool db_disabled_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TrackerDB); |
| +}; |
| + |
| +// LocalFileChangeTracker ------------------------------------------------------ |
| + |
| LocalFileChangeTracker::LocalFileChangeTracker( |
| LocalFileSyncStatus* sync_status, |
| + const FilePath& profile_path, |
| base::SequencedTaskRunner* file_task_runner) |
| : sync_status_(sync_status), |
| - file_task_runner_(file_task_runner) {} |
| + file_task_runner_(file_task_runner), |
| + tracker_db_(new TrackerDB(profile_path)) {} |
| LocalFileChangeTracker::~LocalFileChangeTracker() { |
| - // file_task_runner_->PostTask(FROM_HERE, base::Bind(&DropDatabase)); |
| - // TODO(kinuko): implement. |
| + if (tracker_db_.get()) |
| + file_task_runner_->DeleteSoon(FROM_HERE, tracker_db_.release()); |
| } |
| void LocalFileChangeTracker::OnStartUpdate(const FileSystemURL& url) { |
| @@ -107,11 +157,120 @@ void LocalFileChangeTracker::RecordChange( |
| } |
| void LocalFileChangeTracker::MarkDirtyOnDatabase(const FileSystemURL& url) { |
| - // TODO(kinuko): implement. |
| + tracker_db_->MarkDirty(SerializeExternalFileSystemURL(url)); |
| } |
| void LocalFileChangeTracker::ClearDirtyOnDatabase(const FileSystemURL& url) { |
| - // TODO(kinuko): implement. |
| + tracker_db_->ClearDirty(SerializeExternalFileSystemURL(url)); |
| +} |
| + |
| +std::string LocalFileChangeTracker::SerializeExternalFileSystemURL( |
| + const FileSystemURL& url) { |
| + return GetFileSystemRootURI(url.origin(), kFileSystemTypeExternal).spec() + |
| + url.filesystem_id() + "/" + url.path().AsUTF8Unsafe(); |
| +} |
| + |
| +FileSystemURL LocalFileChangeTracker::DeserializeExternalFileSystemURL( |
| + const std::string& url) { |
| + return FileSystemURL(GURL(url)); |
|
kinuko
2012/09/21 08:12:32
I prefer making this method like:
bool Deserializ
nhiroki
2012/09/24 07:01:40
Done.
|
| +} |
| + |
| +// TrackerDB ------------------------------------------------------------------- |
| + |
| +LocalFileChangeTracker::TrackerDB::TrackerDB(const FilePath& profile_path) |
| + : profile_path_(profile_path), |
| + db_disabled_(false) {} |
| + |
| +bool LocalFileChangeTracker::TrackerDB::Init(RecoveryOption recovery_option) { |
| + if (db_.get()) |
| + return true; |
| + |
| + std::string path = FilePathToString(profile_path_.Append(kDatabaseName)); |
| + leveldb::Options options; |
| + options.create_if_missing = true; |
| + leveldb::DB* db; |
| + leveldb::Status status = leveldb::DB::Open(options, path, &db); |
| + if (status.ok()) { |
| + db_.reset(db); |
| + return true; |
| + } |
| + |
| + HandleError(FROM_HERE, status); |
| + if (!status.IsCorruption()) |
| + return false; |
| + |
| + switch (recovery_option) { |
| + case FAIL_ON_CORRUPTION: |
| + return false; |
| + case REPAIR_ON_CORRUPTION: |
| + return Repair(path); |
| + } |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +bool LocalFileChangeTracker::TrackerDB::Repair(const std::string& db_path) { |
| + DCHECK(!db_.get()); |
| + LOG(WARNING) << "Attempting to repair TrackerDB."; |
| + |
| + if (leveldb::RepairDB(db_path, leveldb::Options()).ok() && |
| + Init(FAIL_ON_CORRUPTION)) { |
| + // TODO(nhiroki): perform some consistency checks between TrackerDB and |
| + // cloud-backed file system. |
|
kinuko
2012/09/21 08:12:32
nit: cloud-backed -> syncable
nhiroki
2012/09/24 07:01:40
Done.
|
| + LOG(WARNING) << "Repairing TrackerDB completed."; |
| + return true; |
| + } |
| + |
| + LOG(WARNING) << "Failed to repair TrackerDB."; |
| + return false; |
| +} |
| + |
| +// TODO(nhiroki): factor out the common methods into somewhere else. |
| +void LocalFileChangeTracker::TrackerDB::HandleError( |
| + const tracked_objects::Location& from_here, |
| + const leveldb::Status& status) { |
| + LOG(ERROR) << "LocalFileChangeTracker::TrackerDB failed at: " |
| + << from_here.ToString() << " with error: " << status.ToString(); |
| +} |
| + |
| +bool LocalFileChangeTracker::TrackerDB::MarkDirty(const std::string& url) { |
| + if (db_disabled_) |
| + return false; |
| + |
| + if (!Init(REPAIR_ON_CORRUPTION)) { |
| + db_disabled_ = true; |
| + db_.reset(); |
| + return false; |
| + } |
| + |
| + leveldb::Status status = db_->Put(leveldb::WriteOptions(), url, kMark); |
| + if (!status.ok()) { |
| + HandleError(FROM_HERE, status); |
| + db_disabled_ = true; |
| + db_.reset(); |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool LocalFileChangeTracker::TrackerDB::ClearDirty(const std::string& url) { |
| + if (db_disabled_) |
| + return false; |
| + |
| + if (!Init(REPAIR_ON_CORRUPTION)) { |
|
kinuko
2012/09/21 08:12:32
Not sure if this could get called before initializ
nhiroki
2012/09/24 07:01:40
I understand. Yes, we can just do DCHECK().
|
| + db_disabled_ = true; |
| + db_.reset(); |
| + return false; |
| + } |
| + |
| + leveldb::Status status = db_->Delete(leveldb::WriteOptions(), url); |
| + if (!status.ok() && !status.IsNotFound()) { |
| + HandleError(FROM_HERE, status); |
| + db_disabled_ = true; |
| + db_.reset(); |
| + return false; |
| + } |
| + return true; |
| } |
| } // namespace fileapi |