blob: 06ea7195b352d8a1ad722a1b66290137a3e73c7d [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h"
#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
#include "chrome/browser/sync_file_system/logger.h"
#include "chrome/browser/sync_file_system/sync_file_system.pb.h"
#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
#include "url/gurl.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/common/fileapi/file_system_util.h"
using fileapi::FileSystemURL;
namespace sync_file_system {
typedef DriveMetadataStore::MetadataMap MetadataMap;
typedef DriveMetadataStore::OriginByResourceId OriginByResourceId;
typedef DriveMetadataStore::PathToMetadata PathToMetadata;
typedef DriveMetadataStore::ResourceIdByOrigin ResourceIdByOrigin;
const base::FilePath::CharType DriveMetadataStore::kDatabaseName[] =
FILE_PATH_LITERAL("DriveMetadata");
struct DBContents {
SyncStatusCode status;
scoped_ptr<leveldb::DB> db;
bool created;
int64 largest_changestamp;
DriveMetadataStore::MetadataMap metadata_map;
std::string sync_root_directory_resource_id;
ResourceIdByOrigin incremental_sync_origins;
ResourceIdByOrigin disabled_origins;
DBContents()
: status(SYNC_STATUS_UNKNOWN),
created(false),
largest_changestamp(0) {
}
};
namespace {
const char kDatabaseVersionKey[] = "VERSION";
const int64 kCurrentDatabaseVersion = 2;
const char kChangeStampKey[] = "CHANGE_STAMP";
const char kSyncRootDirectoryKey[] = "SYNC_ROOT_DIR";
const char kDriveMetadataKeyPrefix[] = "METADATA: ";
const char kMetadataKeySeparator = ' ';
const char kDriveIncrementalSyncOriginKeyPrefix[] = "ISYNC_ORIGIN: ";
const char kDriveDisabledOriginKeyPrefix[] = "DISABLED_ORIGIN: ";
enum OriginSyncType {
INCREMENTAL_SYNC_ORIGIN,
DISABLED_ORIGIN
};
std::string RemovePrefix(const std::string& str, const std::string& prefix) {
if (StartsWithASCII(str, prefix, true))
return str.substr(prefix.size());
return str;
}
std::string OriginAndPathToMetadataKey(const GURL& origin,
const base::FilePath& path) {
return kDriveMetadataKeyPrefix + origin.spec() +
kMetadataKeySeparator + path.AsUTF8Unsafe();
}
std::string FileSystemURLToMetadataKey(const FileSystemURL& url) {
return OriginAndPathToMetadataKey(url.origin(), url.path());
}
void MetadataKeyToOriginAndPath(const std::string& metadata_key,
GURL* origin,
base::FilePath* path) {
std::string key_body(RemovePrefix(metadata_key, kDriveMetadataKeyPrefix));
size_t separator_position = key_body.find(kMetadataKeySeparator);
*origin = GURL(key_body.substr(0, separator_position));
*path = base::FilePath::FromUTF8Unsafe(
key_body.substr(separator_position + 1));
}
bool UpdateResourceIdMap(ResourceIdByOrigin* map,
OriginByResourceId* reverse_map,
const GURL& origin,
const std::string& resource_id) {
ResourceIdByOrigin::iterator found = map->find(origin);
if (found == map->end())
return false;
reverse_map->erase(found->second);
reverse_map->insert(std::make_pair(resource_id, origin));
found->second = resource_id;
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool IsDBEmpty(leveldb::DB* db) {
DCHECK(db);
scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
itr->SeekToFirst();
return !itr->Valid();
}
scoped_ptr<leveldb::DB> OpenDatabase(const base::FilePath& path,
SyncStatusCode* status,
bool* created) {
DCHECK(status);
DCHECK(created);
leveldb::Options options;
options.max_open_files = 0; // Use minimum.
options.create_if_missing = true;
leveldb::DB* db = NULL;
leveldb::Status db_status = leveldb::DB::Open(
options, path.AsUTF8Unsafe(), &db);
if (db_status.ok()) {
*created = IsDBEmpty(db);
} else {
delete db;
db = NULL;
}
*status = LevelDBStatusToSyncStatusCode(db_status);
return make_scoped_ptr(db);
}
SyncStatusCode WriteInitialData(leveldb::DB* db) {
DCHECK(db);
return LevelDBStatusToSyncStatusCode(db->Put(
leveldb::WriteOptions(),
kDatabaseVersionKey,
base::Int64ToString(kCurrentDatabaseVersion)));
}
SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
DCHECK(db);
std::string value;
leveldb::Status status = db->Get(leveldb::ReadOptions(),
kDatabaseVersionKey, &value);
int64 version = 0;
if (status.ok()) {
if (!base::StringToInt64(value, &version))
return SYNC_DATABASE_ERROR_FAILED;
} else {
if (!status.IsNotFound())
return SYNC_DATABASE_ERROR_FAILED;
}
switch (version) {
case 0:
drive_backend::MigrateDatabaseFromV0ToV1(db);
// fall-through
case 1:
drive_backend::MigrateDatabaseFromV1ToV2(db);
// fall-through
case 2:
DCHECK_EQ(2, kCurrentDatabaseVersion);
return SYNC_STATUS_OK;
default:
return SYNC_DATABASE_ERROR_FAILED;
}
}
SyncStatusCode ReadContents(DBContents* contents) {
DCHECK(contents);
DCHECK(contents->db);
contents->largest_changestamp = 0;
contents->metadata_map.clear();
contents->incremental_sync_origins.clear();
scoped_ptr<leveldb::Iterator> itr(
contents->db->NewIterator(leveldb::ReadOptions()));
for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
std::string key = itr->key().ToString();
if (key == kChangeStampKey) {
bool success = base::StringToInt64(itr->value().ToString(),
&contents->largest_changestamp);
DCHECK(success);
continue;
}
if (key == kSyncRootDirectoryKey) {
std::string resource_id = itr->value().ToString();
if (IsDriveAPIDisabled())
resource_id = drive_backend::AddWapiFolderPrefix(resource_id);
contents->sync_root_directory_resource_id = resource_id;
continue;
}
if (StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) {
GURL origin;
base::FilePath path;
MetadataKeyToOriginAndPath(key, &origin, &path);
DriveMetadata metadata;
bool success = metadata.ParseFromString(itr->value().ToString());
DCHECK(success);
if (IsDriveAPIDisabled()) {
metadata.set_resource_id(drive_backend::AddWapiIdPrefix(
metadata.resource_id(), metadata.type()));
}
success = contents->metadata_map[origin].insert(
std::make_pair(path, metadata)).second;
DCHECK(success);
continue;
}
if (StartsWithASCII(key, kDriveIncrementalSyncOriginKeyPrefix, true)) {
GURL origin(RemovePrefix(key, kDriveIncrementalSyncOriginKeyPrefix));
DCHECK(origin.is_valid());
std::string origin_resource_id = IsDriveAPIDisabled()
? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
: itr->value().ToString();
DCHECK(!ContainsKey(contents->incremental_sync_origins, origin));
contents->incremental_sync_origins[origin] = origin_resource_id;
continue;
}
if (StartsWithASCII(key, kDriveDisabledOriginKeyPrefix, true)) {
GURL origin(RemovePrefix(key, kDriveDisabledOriginKeyPrefix));
DCHECK(origin.is_valid());
std::string origin_resource_id = IsDriveAPIDisabled()
? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
: itr->value().ToString();
DCHECK(!ContainsKey(contents->disabled_origins, origin));
contents->disabled_origins[origin] = origin_resource_id;
continue;
}
}
return SYNC_STATUS_OK;
}
scoped_ptr<DBContents> LoadDBContents(const base::FilePath& db_path) {
scoped_ptr<DBContents> contents(new DBContents);
contents->db = OpenDatabase(db_path,
&contents->status,
&contents->created);
if (contents->status != SYNC_STATUS_OK)
return contents.Pass();
if (contents->created) {
contents->status = WriteInitialData(contents->db.get());
if (contents->status != SYNC_STATUS_OK)
return contents.Pass();
} else {
contents->status = MigrateDatabaseIfNeeded(contents->db.get());
if (contents->status != SYNC_STATUS_OK)
return contents.Pass();
}
contents->status = ReadContents(contents.get());
return contents.Pass();
}
////////////////////////////////////////////////////////////////////////////////
// Returns a key string for the given origin.
// For example, when |origin| is "http://www.example.com" and |sync_type| is
// BATCH_SYNC_ORIGIN, returns "BSYNC_ORIGIN: http://www.example.com".
std::string CreateKeyForOriginRoot(const GURL& origin,
OriginSyncType sync_type) {
DCHECK(origin.is_valid());
switch (sync_type) {
case INCREMENTAL_SYNC_ORIGIN:
return kDriveIncrementalSyncOriginKeyPrefix + origin.spec();
case DISABLED_ORIGIN:
return kDriveDisabledOriginKeyPrefix + origin.spec();
}
NOTREACHED();
return std::string();
}
void AddOriginsToVector(std::vector<GURL>* all_origins,
const ResourceIdByOrigin& resource_map) {
for (ResourceIdByOrigin::const_iterator itr = resource_map.begin();
itr != resource_map.end();
++itr) {
all_origins->push_back(itr->first);
}
}
void InsertReverseMap(const ResourceIdByOrigin& forward_map,
OriginByResourceId* backward_map) {
for (ResourceIdByOrigin::const_iterator itr = forward_map.begin();
itr != forward_map.end(); ++itr)
backward_map->insert(std::make_pair(itr->second, itr->first));
}
bool EraseIfExists(ResourceIdByOrigin* map,
const GURL& origin,
std::string* resource_id) {
ResourceIdByOrigin::iterator found = map->find(origin);
if (found == map->end())
return false;
*resource_id = found->second;
map->erase(found);
return true;
}
void AppendMetadataDeletionToBatch(const MetadataMap& metadata_map,
const GURL& origin,
leveldb::WriteBatch* batch) {
MetadataMap::const_iterator found = metadata_map.find(origin);
if (found == metadata_map.end())
return;
for (PathToMetadata::const_iterator itr = found->second.begin();
itr != found->second.end(); ++itr)
batch->Delete(OriginAndPathToMetadataKey(origin, itr->first));
}
std::string DriveTypeToString(DriveMetadata_ResourceType drive_type) {
switch (drive_type) {
case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE:
return "file";
case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER:
return "folder";
}
NOTREACHED();
return "unknown";
}
} // namespace
DriveMetadataStore::DriveMetadataStore(
const base::FilePath& base_dir,
base::SequencedTaskRunner* file_task_runner)
: file_task_runner_(file_task_runner),
base_dir_(base_dir),
db_status_(SYNC_STATUS_UNKNOWN),
largest_changestamp_(0) {
DCHECK(file_task_runner);
}
DriveMetadataStore::~DriveMetadataStore() {
DCHECK(CalledOnValidThread());
file_task_runner_->DeleteSoon(FROM_HERE, db_.release());
}
void DriveMetadataStore::Initialize(const InitializationCallback& callback) {
DCHECK(CalledOnValidThread());
base::PostTaskAndReplyWithResult(
file_task_runner_.get(), FROM_HERE,
base::Bind(&LoadDBContents, base_dir_.Append(kDatabaseName)),
base::Bind(&DriveMetadataStore::DidInitialize, AsWeakPtr(), callback));
}
void DriveMetadataStore::DidInitialize(const InitializationCallback& callback,
scoped_ptr<DBContents> contents) {
DCHECK(CalledOnValidThread());
DCHECK(contents);
db_status_ = contents->status;
if (db_status_ != SYNC_STATUS_OK) {
callback.Run(db_status_, false);
file_task_runner_->DeleteSoon(FROM_HERE, contents.release());
return;
}
db_ = contents->db.Pass();
largest_changestamp_ = contents->largest_changestamp;
metadata_map_.swap(contents->metadata_map);
sync_root_directory_resource_id_ = contents->sync_root_directory_resource_id;
incremental_sync_origins_.swap(contents->incremental_sync_origins);
disabled_origins_.swap(contents->disabled_origins);
origin_by_resource_id_.clear();
InsertReverseMap(incremental_sync_origins_, &origin_by_resource_id_);
InsertReverseMap(disabled_origins_, &origin_by_resource_id_);
callback.Run(db_status_, contents->created);
}
leveldb::DB* DriveMetadataStore::GetDBInstanceForTesting() {
return db_.get();
}
void DriveMetadataStore::SetLargestChangeStamp(
int64 largest_changestamp,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
largest_changestamp_ = largest_changestamp;
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Put(kChangeStampKey, base::Int64ToString(largest_changestamp));
return WriteToDB(batch.Pass(), callback);
}
int64 DriveMetadataStore::GetLargestChangeStamp() const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
return largest_changestamp_;
}
void DriveMetadataStore::UpdateEntry(
const FileSystemURL& url,
const DriveMetadata& metadata,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
DCHECK(!metadata.conflicted() || !metadata.to_be_fetched());
std::pair<PathToMetadata::iterator, bool> result =
metadata_map_[url.origin()].insert(std::make_pair(url.path(), metadata));
if (!result.second)
result.first->second = metadata;
std::string value;
if (IsDriveAPIDisabled()) {
DriveMetadata metadata_in_db(metadata);
metadata_in_db.set_resource_id(
drive_backend::RemoveWapiIdPrefix(metadata.resource_id()));
bool success = metadata_in_db.SerializeToString(&value);
DCHECK(success);
} else {
bool success = metadata.SerializeToString(&value);
DCHECK(success);
}
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Put(FileSystemURLToMetadataKey(url), value);
WriteToDB(batch.Pass(), callback);
}
void DriveMetadataStore::DeleteEntry(
const FileSystemURL& url,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
MetadataMap::iterator found = metadata_map_.find(url.origin());
if (found == metadata_map_.end()) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
return;
}
if (found->second.erase(url.path()) == 1) {
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Delete(FileSystemURLToMetadataKey(url));
WriteToDB(batch.Pass(), callback);
return;
}
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
}
SyncStatusCode DriveMetadataStore::ReadEntry(const FileSystemURL& url,
DriveMetadata* metadata) const {
DCHECK(CalledOnValidThread());
DCHECK(metadata);
MetadataMap::const_iterator found_origin = metadata_map_.find(url.origin());
if (found_origin == metadata_map_.end())
return SYNC_DATABASE_ERROR_NOT_FOUND;
PathToMetadata::const_iterator found = found_origin->second.find(url.path());
if (found == found_origin->second.end())
return SYNC_DATABASE_ERROR_NOT_FOUND;
*metadata = found->second;
return SYNC_STATUS_OK;
}
void DriveMetadataStore::AddIncrementalSyncOrigin(
const GURL& origin,
const std::string& resource_id) {
DCHECK(CalledOnValidThread());
DCHECK(!IsIncrementalSyncOrigin(origin));
DCHECK(!IsOriginDisabled(origin));
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
incremental_sync_origins_.insert(std::make_pair(origin, resource_id));
origin_by_resource_id_.insert(std::make_pair(resource_id, origin));
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
batch->Put(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN),
drive_backend::RemoveWapiIdPrefix(resource_id));
WriteToDB(batch.Pass(),
base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
}
void DriveMetadataStore::SetSyncRootDirectory(const std::string& resource_id) {
DCHECK(CalledOnValidThread());
sync_root_directory_resource_id_ = resource_id;
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Put(kSyncRootDirectoryKey,
drive_backend::RemoveWapiIdPrefix(resource_id));
return WriteToDB(batch.Pass(),
base::Bind(&DriveMetadataStore::UpdateDBStatus,
AsWeakPtr()));
}
void DriveMetadataStore::SetOriginRootDirectory(
const GURL& origin,
const std::string& resource_id) {
DCHECK(CalledOnValidThread());
DCHECK(IsKnownOrigin(origin));
OriginSyncType sync_type;
if (UpdateResourceIdMap(
&incremental_sync_origins_, &origin_by_resource_id_,
origin, resource_id)) {
sync_type = INCREMENTAL_SYNC_ORIGIN;
} else if (UpdateResourceIdMap(&disabled_origins_, &origin_by_resource_id_,
origin, resource_id)) {
sync_type = DISABLED_ORIGIN;
} else {
return;
}
std::string key = CreateKeyForOriginRoot(origin, sync_type);
DCHECK(!key.empty());
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Put(key, drive_backend::RemoveWapiIdPrefix(resource_id));
WriteToDB(batch.Pass(),
base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
}
bool DriveMetadataStore::IsKnownOrigin(const GURL& origin) const {
DCHECK(CalledOnValidThread());
return IsIncrementalSyncOrigin(origin) || IsOriginDisabled(origin);
}
bool DriveMetadataStore::IsIncrementalSyncOrigin(const GURL& origin) const {
DCHECK(CalledOnValidThread());
return ContainsKey(incremental_sync_origins_, origin);
}
bool DriveMetadataStore::IsOriginDisabled(const GURL& origin) const {
DCHECK(CalledOnValidThread());
return ContainsKey(disabled_origins_, origin);
}
void DriveMetadataStore::EnableOrigin(
const GURL& origin,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
std::map<GURL, std::string>::iterator found = disabled_origins_.find(origin);
if (found == disabled_origins_.end()) {
// |origin| has not been registered yet.
return;
}
disabled_origins_.erase(found);
// |origin| goes back to DriveFileSyncService::pending_batch_sync_origins_
// only and is not stored in drive_metadata_store.
found = incremental_sync_origins_.find(origin);
if (found != incremental_sync_origins_.end())
incremental_sync_origins_.erase(found);
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
WriteToDB(batch.Pass(), callback);
}
void DriveMetadataStore::DisableOrigin(
const GURL& origin,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
std::string resource_id;
if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id))
return;
disabled_origins_[origin] = resource_id;
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
batch->Put(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN),
drive_backend::RemoveWapiIdPrefix(resource_id));
AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
metadata_map_.erase(origin);
WriteToDB(batch.Pass(), callback);
}
void DriveMetadataStore::RemoveOrigin(
const GURL& origin,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
std::string resource_id;
if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id) &&
!EraseIfExists(&disabled_origins_, origin, &resource_id))
return;
origin_by_resource_id_.erase(resource_id);
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
metadata_map_.erase(origin);
WriteToDB(batch.Pass(), callback);
}
void DriveMetadataStore::DidUpdateOrigin(
const SyncStatusCallback& callback,
SyncStatusCode status) {
UpdateDBStatus(status);
callback.Run(status);
}
void DriveMetadataStore::WriteToDB(scoped_ptr<leveldb::WriteBatch> batch,
const SyncStatusCallback& callback) {
DCHECK(CalledOnValidThread());
if (db_status_ != SYNC_STATUS_OK &&
db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_FAILED));
return;
}
DCHECK(db_);
base::PostTaskAndReplyWithResult(
file_task_runner_.get(),
FROM_HERE,
base::Bind(&leveldb::DB::Write,
base::Unretained(db_.get()),
leveldb::WriteOptions(),
base::Owned(batch.release())),
base::Bind(&DriveMetadataStore::UpdateDBStatusAndInvokeCallback,
AsWeakPtr(),
callback));
}
void DriveMetadataStore::UpdateDBStatus(SyncStatusCode status) {
DCHECK(CalledOnValidThread());
if (db_status_ != SYNC_STATUS_OK &&
db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
// TODO(tzik): Handle database corruption. http://crbug.com/153709
db_status_ = status;
util::Log(logging::LOG_WARNING,
FROM_HERE,
"DriveMetadataStore turned to wrong state: %s",
SyncStatusCodeToString(status));
return;
}
db_status_ = SYNC_STATUS_OK;
}
void DriveMetadataStore::UpdateDBStatusAndInvokeCallback(
const SyncStatusCallback& callback,
const leveldb::Status& leveldb_status) {
SyncStatusCode status = LevelDBStatusToSyncStatusCode(leveldb_status);
UpdateDBStatus(status);
callback.Run(status);
}
SyncStatusCode DriveMetadataStore::GetConflictURLs(
fileapi::FileSystemURLSet* urls) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
urls->clear();
for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
origin_itr != metadata_map_.end();
++origin_itr) {
for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
itr != origin_itr->second.end();
++itr) {
if (itr->second.conflicted()) {
urls->insert(CreateSyncableFileSystemURL(
origin_itr->first, itr->first));
}
}
}
return SYNC_STATUS_OK;
}
SyncStatusCode DriveMetadataStore::GetToBeFetchedFiles(
URLAndDriveMetadataList* list) const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(SYNC_STATUS_OK, db_status_);
list->clear();
for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
origin_itr != metadata_map_.end();
++origin_itr) {
for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
itr != origin_itr->second.end();
++itr) {
if (itr->second.to_be_fetched()) {
FileSystemURL url = CreateSyncableFileSystemURL(
origin_itr->first, itr->first);
list->push_back(std::make_pair(url, itr->second));
}
}
}
return SYNC_STATUS_OK;
}
std::string DriveMetadataStore::GetResourceIdForOrigin(
const GURL& origin) const {
DCHECK(CalledOnValidThread());
// If we don't have valid root directory (this could be reset even after
// initialized) just return empty string, as the origin directories
// in the root directory must have become invalid now too.
if (sync_root_directory().empty())
return std::string();
ResourceIdByOrigin::const_iterator found =
incremental_sync_origins_.find(origin);
if (found != incremental_sync_origins_.end())
return found->second;
found = disabled_origins_.find(origin);
if (found != disabled_origins_.end())
return found->second;
return std::string();
}
void DriveMetadataStore::GetAllOrigins(std::vector<GURL>* origins) {
DCHECK(CalledOnValidThread());
DCHECK(origins);
origins->clear();
origins->reserve(incremental_sync_origins_.size() +
disabled_origins_.size());
AddOriginsToVector(origins, incremental_sync_origins_);
AddOriginsToVector(origins, disabled_origins_);
}
bool DriveMetadataStore::GetOriginByOriginRootDirectoryId(
const std::string& resource_id,
GURL* origin) {
DCHECK(CalledOnValidThread());
DCHECK(origin);
OriginByResourceId::iterator found = origin_by_resource_id_.find(resource_id);
if (found == origin_by_resource_id_.end())
return false;
*origin = found->second;
return true;
}
scoped_ptr<base::ListValue> DriveMetadataStore::DumpFiles(const GURL& origin) {
DCHECK(CalledOnValidThread());
scoped_ptr<base::ListValue> files(new base::ListValue);
MetadataMap::const_iterator found = metadata_map_.find(origin);
if (found == metadata_map_.end())
return make_scoped_ptr(new base::ListValue);
for (PathToMetadata::const_iterator itr = found->second.begin();
itr != found->second.end();
++itr) {
// Convert Drive specific metadata to Common File metadata object.
const DriveMetadata& metadata = itr->second;
base::DictionaryValue* file = new DictionaryValue;
file->SetString("path", itr->first.AsUTF8Unsafe());
file->SetString("title", itr->first.BaseName().AsUTF8Unsafe());
file->SetString("type", DriveTypeToString(metadata.type()));
base::DictionaryValue* details = new DictionaryValue;
details->SetString("resource_id", metadata.resource_id());
details->SetString("md5", metadata.md5_checksum());
details->SetString("dirty", metadata.to_be_fetched() ? "true" : "false");
file->Set("details", details);
files->Append(file);
}
return files.Pass();
}
} // namespace sync_file_system