blob: eb9c18ca94e206ed396a5fbf849f3132ab402fb8 [file] [log] [blame]
// Copyright (c) 2012 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/chromeos/gdata/drive_resource_metadata.h"
#include <leveldb/db.h>
#include <utility>
#include "base/message_loop_proxy.h"
#include "base/string_number_conversions.h"
#include "base/sequenced_task_runner.h"
#include "base/tracked_objects.h"
#include "chrome/browser/chromeos/gdata/drive.pb.h"
#include "chrome/browser/chromeos/gdata/drive_files.h"
#include "chrome/browser/google_apis/gdata_util.h"
#include "chrome/browser/google_apis/gdata_wapi_parser.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace gdata {
namespace {
// m: prefix for filesystem metadata db keys, version and largest_changestamp.
// r: prefix for resource id db keys.
const char kDBKeyLargestChangestamp[] = "m:largest_changestamp";
const char kDBKeyVersion[] = "m:version";
const char kDBKeyResourceIdPrefix[] = "r:";
// Posts |error| to |callback| asynchronously. |callback| must not be null.
void PostFileMoveCallbackError(const FileMoveCallback& callback,
DriveFileError error) {
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, error, FilePath()));
}
// Posts |error| to |callback| asynchronously. |callback| must not be null.
void PostGetEntryInfoWithFilePathCallbackError(
const GetEntryInfoWithFilePathCallback& callback,
DriveFileError error) {
scoped_ptr<DriveEntryProto> proto;
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, error, FilePath(), base::Passed(&proto)));
}
} // namespace
EntryInfoResult::EntryInfoResult() : error(DRIVE_FILE_ERROR_FAILED) {
}
EntryInfoResult::~EntryInfoResult() {
}
EntryInfoPairResult::EntryInfoPairResult() {
}
EntryInfoPairResult::~EntryInfoPairResult() {
}
// ResourceMetadataDB implementation.
// Params for ResourceMetadataDB::Create.
struct CreateDBParams {
CreateDBParams(const FilePath& db_path,
base::SequencedTaskRunner* blocking_task_runner)
: db_path(db_path),
blocking_task_runner(blocking_task_runner) {
}
FilePath db_path;
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
scoped_ptr<ResourceMetadataDB> db;
DriveResourceMetadata::SerializedMap serialized_resources;
};
// Wrapper for level db. All methods must be called on blocking thread.
class ResourceMetadataDB {
public:
ResourceMetadataDB(const FilePath& db_path,
base::SequencedTaskRunner* blocking_task_runner);
// Initializes the database.
void Init();
// Reads the database into |serialized_resources|.
void Read(DriveResourceMetadata::SerializedMap* serialized_resources);
// Saves |serialized_resources| to the database.
void Save(const DriveResourceMetadata::SerializedMap& serialized_resources);
private:
// Clears the database.
void Clear();
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
scoped_ptr<leveldb::DB> level_db_;
FilePath db_path_;
};
ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path,
base::SequencedTaskRunner* blocking_task_runner)
: blocking_task_runner_(blocking_task_runner),
db_path_(db_path) {
DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
}
// Creates, initializes and reads from the database.
// This must be defined after ResourceMetadataDB and CreateDBParams.
static void CreateResourceMetadataDBOnBlockingPool(
CreateDBParams* params) {
DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
DCHECK(!params->db_path.empty());
params->db.reset(new ResourceMetadataDB(params->db_path,
params->blocking_task_runner));
params->db->Init();
params->db->Read(&params->serialized_resources);
}
void ResourceMetadataDB::Init() {
DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
DCHECK(!db_path_.empty());
DVLOG(1) << "Init " << db_path_.value();
leveldb::DB* level_db = NULL;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(),
&level_db);
DCHECK(level_db);
DCHECK(db_status.ok());
level_db_.reset(level_db);
}
void ResourceMetadataDB::Read(
DriveResourceMetadata::SerializedMap* serialized_resources) {
DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
DCHECK(serialized_resources);
DVLOG(1) << "Read " << db_path_.value();
scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
leveldb::ReadOptions()));
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
DVLOG(1) << "Read, resource " << iter->key().ToString();
serialized_resources->insert(std::make_pair(iter->key().ToString(),
iter->value().ToString()));
}
}
void ResourceMetadataDB::Save(
const DriveResourceMetadata::SerializedMap& serialized_resources) {
DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
Clear();
for (DriveResourceMetadata::SerializedMap::const_iterator iter =
serialized_resources.begin();
iter != serialized_resources.end(); ++iter) {
DVLOG(1) << "Saving resource " << iter->first << " to db";
leveldb::Status status = level_db_->Put(leveldb::WriteOptions(),
leveldb::Slice(iter->first),
leveldb::Slice(iter->second));
if (!status.ok()) {
LOG(ERROR) << "leveldb Put failed of " << iter->first
<< ", with " << status.ToString();
NOTREACHED();
}
}
}
void ResourceMetadataDB::Clear() {
level_db_.reset();
leveldb::DestroyDB(db_path_.value(), leveldb::Options());
Init();
}
// DriveResourceMetadata class implementation.
DriveResourceMetadata::DriveResourceMetadata()
: blocking_task_runner_(NULL),
serialized_size_(0),
largest_changestamp_(0),
origin_(UNINITIALIZED),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
root_ = CreateDriveDirectory().Pass();
if (!util::IsDriveV2ApiEnabled())
InitializeRootEntry(kDriveRootDirectoryResourceId);
}
DriveResourceMetadata::~DriveResourceMetadata() {
ClearRoot();
// Ensure db is closed on the blocking pool.
if (blocking_task_runner_ && resource_metadata_db_.get())
blocking_task_runner_->DeleteSoon(FROM_HERE,
resource_metadata_db_.release());
}
scoped_ptr<DriveEntry> DriveResourceMetadata::FromDocumentEntry(
const DocumentEntry& doc) {
scoped_ptr<DriveEntry> entry;
if (doc.is_folder())
entry = CreateDriveDirectory().Pass();
else if (doc.is_hosted_document() || doc.is_file())
entry = CreateDriveFile().Pass();
if (entry.get())
entry->InitFromDocumentEntry(doc);
return entry.Pass();
}
scoped_ptr<DriveFile> DriveResourceMetadata::CreateDriveFile() {
return scoped_ptr<DriveFile>(new DriveFile(this));
}
scoped_ptr<DriveDirectory> DriveResourceMetadata::CreateDriveDirectory() {
return scoped_ptr<DriveDirectory>(new DriveDirectory(this));
}
void DriveResourceMetadata::InitializeRootEntry(const std::string& root_id) {
root_ = CreateDriveDirectory().Pass();
root_->set_title(kDriveRootDirectory);
root_->SetBaseNameFromTitle();
root_->set_resource_id(root_id);
AddEntryToResourceMap(root_.get());
}
void DriveResourceMetadata::ClearRoot() {
if (!root_.get())
return;
// Note that children have a reference to root_,
// so we need to delete them here.
root_->RemoveChildren();
RemoveEntryFromResourceMap(root_->resource_id());
DCHECK(resource_map_.empty());
resource_map_.clear();
root_.reset();
}
void DriveResourceMetadata::AddEntryToDirectory(
const FilePath& directory_path,
scoped_ptr<DocumentEntry> doc_entry,
const FileMoveCallback& callback) {
DCHECK(!directory_path.empty());
DCHECK(!callback.is_null());
if (!doc_entry.get()) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_FAILED);
return;
}
scoped_ptr<DriveEntry> new_entry = FromDocumentEntry(*doc_entry);
if (!new_entry.get()) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_FAILED);
return;
}
DriveEntry* dir_entry = FindEntryByPathSync(directory_path);
if (!dir_entry) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
DriveDirectory* directory = dir_entry->AsDriveDirectory();
if (!directory) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_A_DIRECTORY);
return;
}
DriveEntry* added_entry = new_entry.release();
directory->AddEntry(added_entry); // Transfers ownership.
DVLOG(1) << "AddEntryToDirectory " << added_entry->GetFilePath().value();
base::MessageLoopProxy::current()->PostTask(FROM_HERE,
base::Bind(callback, DRIVE_FILE_OK, added_entry->GetFilePath()));
}
void DriveResourceMetadata::MoveEntryToDirectory(
const FilePath& file_path,
const FilePath& directory_path,
const FileMoveCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!directory_path.empty());
DCHECK(!file_path.empty());
DCHECK(!callback.is_null());
DriveEntry* entry = FindEntryByPathSync(file_path);
if (!entry) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
DriveEntry* destination = FindEntryByPathSync(directory_path);
FilePath moved_file_path;
DriveFileError error = DRIVE_FILE_ERROR_FAILED;
if (!destination) {
error = DRIVE_FILE_ERROR_NOT_FOUND;
} else if (!destination->AsDriveDirectory()) {
error = DRIVE_FILE_ERROR_NOT_A_DIRECTORY;
} else {
if (entry->parent())
entry->parent()->RemoveChild(entry);
destination->AsDriveDirectory()->AddEntry(entry);
moved_file_path = entry->GetFilePath();
error = DRIVE_FILE_OK;
}
DVLOG(1) << "MoveEntryToDirectory " << moved_file_path.value();
base::MessageLoopProxy::current()->PostTask(
FROM_HERE, base::Bind(callback, error, moved_file_path));
}
void DriveResourceMetadata::RenameEntry(
const FilePath& file_path,
const FilePath::StringType& new_name,
const FileMoveCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!file_path.empty());
DCHECK(!new_name.empty());
DCHECK(!callback.is_null());
DVLOG(1) << "RenameEntry " << file_path.value() << " to " << new_name;
DriveEntry* entry = FindEntryByPathSync(file_path);
if (!entry) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
if (new_name == file_path.BaseName().value()) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_EXISTS);
return;
}
entry->set_title(new_name);
DCHECK(entry->parent());
// After changing the title of the entry, call MoveEntryToDirectory to
// remove the entry from its parent directory and then add it back in order to
// go through the file name de-duplication.
// TODO(achuith/satorux/zel): This code is fragile. The title has been
// changed, but not the file_name. MoveEntryToDirectory calls RemoveChild to
// remove the child based on the old file_name, and then re-adds the child by
// first assigning the new title to file_name. http://crbug.com/30157
MoveEntryToDirectory(file_path, entry->parent()->GetFilePath(), callback);
}
void DriveResourceMetadata::RemoveEntryFromParent(
const std::string& resource_id,
const FileMoveCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
// Disallow deletion of root.
if (resource_id == kDriveRootDirectoryResourceId) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_ACCESS_DENIED);
return;
}
DriveEntry* entry = GetEntryByResourceId(resource_id);
if (!entry) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
DriveDirectory* parent = entry->parent();
DCHECK(parent);
DVLOG(1) << "RemoveEntryFromParent " << entry->GetFilePath().value();
parent->RemoveEntry(entry);
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, DRIVE_FILE_OK, parent->GetFilePath()));
}
void DriveResourceMetadata::AddEntryToResourceMap(DriveEntry* entry) {
DVLOG(1) << "AddEntryToResourceMap " << entry->resource_id();
DCHECK(!entry->resource_id().empty());
std::pair<ResourceMap::iterator, bool> ret =
resource_map_.insert(std::make_pair(entry->resource_id(), entry));
DCHECK(ret.second); // resource_id did not previously exist in the map.
}
void DriveResourceMetadata::RemoveEntryFromResourceMap(
const std::string& resource_id) {
DVLOG(1) << "RemoveEntryFromResourceMap " << resource_id;
DCHECK(!resource_id.empty());
size_t ret = resource_map_.erase(resource_id);
DCHECK_EQ(1u, ret); // resource_id was found in the map.
}
DriveEntry* DriveResourceMetadata::FindEntryByPathSync(
const FilePath& file_path) {
if (file_path == root_->GetFilePath())
return root_.get();
std::vector<FilePath::StringType> components;
file_path.GetComponents(&components);
DriveDirectory* current_dir = root_.get();
for (size_t i = 1; i < components.size() && current_dir; ++i) {
std::string resource_id = current_dir->FindChild(components[i]);
if (resource_id.empty())
return NULL;
DriveEntry* entry = GetEntryByResourceId(resource_id);
DCHECK(entry);
if (i == components.size() - 1) // Last component.
return entry;
else
current_dir = entry->AsDriveDirectory();
}
return NULL;
}
DriveEntry* DriveResourceMetadata::GetEntryByResourceId(
const std::string& resource_id) {
DCHECK(!resource_id.empty());
ResourceMap::const_iterator iter = resource_map_.find(resource_id);
return iter == resource_map_.end() ? NULL : iter->second;
}
void DriveResourceMetadata::GetEntryInfoByResourceId(
const std::string& resource_id,
const GetEntryInfoWithFilePathCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
scoped_ptr<DriveEntryProto> entry_proto;
DriveFileError error = DRIVE_FILE_ERROR_FAILED;
FilePath drive_file_path;
DriveEntry* entry = GetEntryByResourceId(resource_id);
if (entry) {
entry_proto.reset(new DriveEntryProto);
entry->ToProtoFull(entry_proto.get());
error = DRIVE_FILE_OK;
drive_file_path = entry->GetFilePath();
} else {
error = DRIVE_FILE_ERROR_NOT_FOUND;
}
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, error, drive_file_path, base::Passed(&entry_proto)));
}
void DriveResourceMetadata::GetEntryInfoByPath(
const FilePath& path,
const GetEntryInfoCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
scoped_ptr<DriveEntryProto> entry_proto;
DriveFileError error = DRIVE_FILE_ERROR_FAILED;
DriveEntry* entry = FindEntryByPathSync(path);
if (entry) {
entry_proto.reset(new DriveEntryProto);
entry->ToProtoFull(entry_proto.get());
error = DRIVE_FILE_OK;
} else {
error = DRIVE_FILE_ERROR_NOT_FOUND;
}
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, error, base::Passed(&entry_proto)));
}
void DriveResourceMetadata::ReadDirectoryByPath(
const FilePath& path,
const ReadDirectoryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
scoped_ptr<DriveEntryProtoVector> entries;
DriveFileError error = DRIVE_FILE_ERROR_FAILED;
DriveEntry* entry = FindEntryByPathSync(path);
if (entry && entry->AsDriveDirectory()) {
entries = entry->AsDriveDirectory()->ToProtoVector();
error = DRIVE_FILE_OK;
} else if (entry && !entry->AsDriveDirectory()) {
error = DRIVE_FILE_ERROR_NOT_A_DIRECTORY;
} else {
error = DRIVE_FILE_ERROR_NOT_FOUND;
}
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, error, base::Passed(&entries)));
}
void DriveResourceMetadata::GetEntryInfoPairByPaths(
const FilePath& first_path,
const FilePath& second_path,
const GetEntryInfoPairCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
// Get the first entry.
GetEntryInfoByPath(
first_path,
base::Bind(&DriveResourceMetadata::GetEntryInfoPairByPathsAfterGetFirst,
weak_ptr_factory_.GetWeakPtr(),
first_path,
second_path,
callback));
}
void DriveResourceMetadata::RefreshFile(
scoped_ptr<DocumentEntry> doc_entry,
const GetEntryInfoWithFilePathCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (!doc_entry.get()) {
PostGetEntryInfoWithFilePathCallbackError(
callback, DRIVE_FILE_ERROR_FAILED);
return;
}
scoped_ptr<DriveEntry> drive_entry(FromDocumentEntry(*doc_entry));
if (!drive_entry.get()) {
PostGetEntryInfoWithFilePathCallbackError(
callback, DRIVE_FILE_ERROR_FAILED);
return;
}
if (!drive_entry->AsDriveFile()) {
// This is a directory, return the directory info instead.
GetEntryInfoByResourceId(drive_entry->resource_id(), callback);
return;
}
scoped_ptr<DriveFile> fresh_file(drive_entry.release()->AsDriveFile());
// Need to get a reference here because Passed() could get evaluated first.
const std::string& resource_id = fresh_file->resource_id();
DVLOG(1) << "RefreshFile " << resource_id;
DriveEntry* old_entry = GetEntryByResourceId(resource_id);
DriveDirectory* entry_parent = old_entry ? old_entry->parent() : NULL;
if (!entry_parent) {
PostGetEntryInfoWithFilePathCallbackError(
callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id());
DCHECK(old_entry->AsDriveFile());
entry_parent->RemoveEntry(old_entry);
DriveEntry* new_entry = fresh_file.release();
entry_parent->AddEntry(new_entry);
scoped_ptr<DriveEntryProto> entry_proto(new DriveEntryProto);
new_entry->ToProtoFull(entry_proto.get());
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback,
DRIVE_FILE_OK,
new_entry->GetFilePath(),
base::Passed(&entry_proto)));
}
void DriveResourceMetadata::RefreshDirectory(
const std::string& directory_resource_id,
const ResourceMap& file_map,
const FileMoveCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
DriveEntry* directory_entry = GetEntryByResourceId(directory_resource_id);
if (!directory_entry) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
DriveDirectory* directory = directory_entry->AsDriveDirectory();
if (!directory) {
PostFileMoveCallbackError(callback, DRIVE_FILE_ERROR_NOT_A_DIRECTORY);
return;
}
directory->RemoveChildFiles();
// Add files from file_map.
for (ResourceMap::const_iterator it = file_map.begin();
it != file_map.end(); ++it) {
scoped_ptr<DriveEntry> entry(it->second);
// Skip if it's not a file (i.e. directory).
if (!entry->AsDriveFile())
continue;
directory->AddEntry(entry.release());
}
base::MessageLoopProxy::current()->PostTask(
FROM_HERE,
base::Bind(callback, DRIVE_FILE_OK, directory->GetFilePath()));
}
void DriveResourceMetadata::InitFromDB(
const FilePath& db_path,
base::SequencedTaskRunner* blocking_task_runner,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!db_path.empty());
DCHECK(blocking_task_runner);
DCHECK(!callback.is_null());
if (resource_metadata_db_.get()) {
callback.Run(DRIVE_FILE_ERROR_IN_USE);
return;
}
blocking_task_runner_ = blocking_task_runner;
DVLOG(1) << "InitFromDB " << db_path.value();
CreateDBParams* create_params =
new CreateDBParams(db_path, blocking_task_runner);
blocking_task_runner_->PostTaskAndReply(
FROM_HERE,
base::Bind(&CreateResourceMetadataDBOnBlockingPool,
create_params),
base::Bind(&DriveResourceMetadata::InitResourceMap,
weak_ptr_factory_.GetWeakPtr(),
base::Owned(create_params),
callback));
}
void DriveResourceMetadata::InitResourceMap(
CreateDBParams* create_params,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(create_params);
DCHECK(!resource_metadata_db_.get());
DCHECK(!callback.is_null());
SerializedMap* serialized_resources = &create_params->serialized_resources;
resource_metadata_db_ = create_params->db.Pass();
if (serialized_resources->empty()) {
origin_ = INITIALIZING;
callback.Run(DRIVE_FILE_ERROR_NOT_FOUND);
return;
}
ClearRoot();
// Version check.
int32 version = 0;
SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion);
if (iter == serialized_resources->end() ||
!base::StringToInt(iter->second, &version) ||
version != kProtoVersion) {
callback.Run(DRIVE_FILE_ERROR_FAILED);
return;
}
serialized_resources->erase(iter);
// Get the largest changestamp.
iter = serialized_resources->find(kDBKeyLargestChangestamp);
if (iter == serialized_resources->end() ||
!base::StringToInt64(iter->second, &largest_changestamp_)) {
NOTREACHED() << "Could not find/parse largest_changestamp";
callback.Run(DRIVE_FILE_ERROR_FAILED);
return;
} else {
DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_;
serialized_resources->erase(iter);
}
ResourceMap resource_map;
for (SerializedMap::const_iterator iter = serialized_resources->begin();
iter != serialized_resources->end(); ++iter) {
if (iter->first.find(kDBKeyResourceIdPrefix) != 0) {
NOTREACHED() << "Incorrect prefix for db key " << iter->first;
continue;
}
const std::string resource_id =
iter->first.substr(strlen(kDBKeyResourceIdPrefix));
scoped_ptr<DriveEntry> entry = FromProtoString(iter->second);
if (entry.get()) {
DVLOG(1) << "Inserting resource " << resource_id
<< " into resource_map";
resource_map.insert(std::make_pair(resource_id, entry.release()));
} else {
NOTREACHED() << "Failed to parse DriveEntry for resource " << resource_id;
}
}
// Fix up parent-child relations.
for (ResourceMap::iterator iter = resource_map.begin();
iter != resource_map.end(); ++iter) {
DriveEntry* entry = iter->second;
ResourceMap::iterator parent_it =
resource_map.find(entry->parent_resource_id());
if (parent_it != resource_map.end()) {
DriveDirectory* parent = parent_it->second->AsDriveDirectory();
if (parent) {
DVLOG(1) << "Adding " << entry->resource_id()
<< " as a child of " << parent->resource_id();
parent->AddEntry(entry);
} else {
NOTREACHED() << "Parent is not a directory " << parent->resource_id();
}
} else if (entry->resource_id() == kDriveRootDirectoryResourceId) {
root_.reset(entry->AsDriveDirectory());
DCHECK(root_.get());
AddEntryToResourceMap(root_.get());
} else {
NOTREACHED() << "Missing parent id " << entry->parent_resource_id()
<< " for resource " << entry->resource_id();
}
}
if (!root_.get()) {
// TODO(achuith): Initialize |root_| before return.
callback.Run(DRIVE_FILE_ERROR_FAILED);
return;
}
DCHECK_EQ(resource_map.size(), resource_map_.size());
DCHECK_EQ(resource_map.size(), serialized_resources->size());
origin_ = FROM_CACHE;
callback.Run(DRIVE_FILE_OK);
}
void DriveResourceMetadata::SaveToDB() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!blocking_task_runner_ || !resource_metadata_db_.get()) {
NOTREACHED();
return;
}
size_t serialized_size = 0;
SerializedMap serialized_resources;
for (ResourceMap::const_iterator iter = resource_map_.begin();
iter != resource_map_.end(); ++iter) {
DriveEntryProto proto;
iter->second->ToProtoFull(&proto);
std::string serialized_string;
const bool ok = proto.SerializeToString(&serialized_string);
DCHECK(ok);
if (ok) {
serialized_resources.insert(
std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first,
serialized_string));
serialized_size += serialized_string.size();
}
}
serialized_resources.insert(std::make_pair(kDBKeyVersion,
base::IntToString(kProtoVersion)));
serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp,
base::IntToString(largest_changestamp_)));
set_last_serialized(base::Time::Now());
set_serialized_size(serialized_size);
blocking_task_runner_->PostTask(
FROM_HERE,
base::Bind(&ResourceMetadataDB::Save,
base::Unretained(resource_metadata_db_.get()),
serialized_resources));
}
void DriveResourceMetadata::SerializeToString(
std::string* serialized_proto) const {
DriveRootDirectoryProto proto;
root_->ToProto(proto.mutable_drive_directory());
proto.set_largest_changestamp(largest_changestamp_);
proto.set_version(kProtoVersion);
const bool ok = proto.SerializeToString(serialized_proto);
DCHECK(ok);
}
bool DriveResourceMetadata::ParseFromString(
const std::string& serialized_proto) {
DriveRootDirectoryProto proto;
if (!proto.ParseFromString(serialized_proto))
return false;
if (proto.version() != kProtoVersion) {
LOG(ERROR) << "Incompatible proto detected (incompatible version): "
<< proto.version();
return false;
}
root_->FromProto(proto.drive_directory());
origin_ = FROM_CACHE;
largest_changestamp_ = proto.largest_changestamp();
return true;
}
scoped_ptr<DriveEntry> DriveResourceMetadata::FromProtoString(
const std::string& serialized_proto) {
DriveEntryProto entry_proto;
if (!entry_proto.ParseFromString(serialized_proto))
return scoped_ptr<DriveEntry>();
scoped_ptr<DriveEntry> entry;
if (entry_proto.file_info().is_directory()) {
entry = CreateDriveDirectory().Pass();
// Call DriveEntry::FromProto instead of DriveDirectory::FromProto because
// the proto does not include children.
entry->FromProto(entry_proto);
} else {
scoped_ptr<DriveFile> file(CreateDriveFile());
// Call DriveFile::FromProto.
file->FromProto(entry_proto);
entry.reset(file.release());
}
return entry.Pass();
}
void DriveResourceMetadata::GetEntryInfoPairByPathsAfterGetFirst(
const FilePath& first_path,
const FilePath& second_path,
const GetEntryInfoPairCallback& callback,
DriveFileError error,
scoped_ptr<DriveEntryProto> entry_proto) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult);
result->first.path = first_path;
result->first.error = error;
result->first.proto = entry_proto.Pass();
// If the first one is not found, don't continue.
if (error != DRIVE_FILE_OK) {
callback.Run(result.Pass());
return;
}
// Get the second entry.
GetEntryInfoByPath(
second_path,
base::Bind(&DriveResourceMetadata::GetEntryInfoPairByPathsAfterGetSecond,
weak_ptr_factory_.GetWeakPtr(),
second_path,
callback,
base::Passed(&result)));
}
void DriveResourceMetadata::GetEntryInfoPairByPathsAfterGetSecond(
const FilePath& second_path,
const GetEntryInfoPairCallback& callback,
scoped_ptr<EntryInfoPairResult> result,
DriveFileError error,
scoped_ptr<DriveEntryProto> entry_proto) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
DCHECK(result.get());
result->second.path = second_path;
result->second.error = error;
result->second.proto = entry_proto.Pass();
callback.Run(result.Pass());
}
} // namespace gdata