blob: 69da4f995a15ace0ea6d5b762ee19c2ba76757ab [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/local_to_remote_syncer.h"
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/format_macros.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "chrome/browser/drive/drive_api_util.h"
#include "chrome/browser/drive/drive_service_interface.h"
#include "chrome/browser/drive/drive_uploader.h"
#include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
#include "chrome/browser/sync_file_system/drive_backend/folder_creator.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
#include "chrome/browser/sync_file_system/logger.h"
#include "google_apis/drive/drive_api_parser.h"
#include "net/base/mime_util.h"
#include "webkit/common/fileapi/file_system_util.h"
namespace sync_file_system {
namespace drive_backend {
namespace {
scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
int64 tracker_id) {
scoped_ptr<FileTracker> tracker(new FileTracker);
if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
return tracker.Pass();
return scoped_ptr<FileTracker>();
}
bool GetKnownChangeID(MetadataDatabase* metadata_database,
const std::string& file_id,
int64* change_id) {
FileMetadata remote_file_metadata;
if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
return false;
*change_id = remote_file_metadata.details().change_id();
return true;
}
bool IsLocalFileMissing(const SyncFileMetadata& local_metadata,
const FileChange& local_change) {
return local_metadata.file_type == SYNC_FILE_TYPE_UNKNOWN ||
local_change.IsDelete();
}
std::string GetMimeTypeFromTitle(const base::FilePath& title) {
base::FilePath::StringType extension = title.Extension();
std::string mime_type;
if (extension.empty() ||
!net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
return kMimeTypeOctetStream;
return mime_type;
}
} // namespace
LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
const SyncFileMetadata& local_metadata,
const FileChange& local_change,
const base::FilePath& local_path,
const fileapi::FileSystemURL& url)
: sync_context_(sync_context),
local_change_(local_change),
local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
local_path_(local_path),
url_(url),
sync_action_(SYNC_ACTION_NONE),
remote_file_change_id_(0),
retry_on_success_(false),
needs_remote_change_listing_(false),
weak_ptr_factory_(this) {
DCHECK(local_is_missing_ ||
local_change.file_type() == local_metadata.file_type)
<< local_change.DebugString() << " metadata:" << local_metadata.file_type;
}
LocalToRemoteSyncer::~LocalToRemoteSyncer() {
}
void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
token->InitializeTaskLog("Local -> Remote");
if (!IsContextReady()) {
token->RecordLog("Context not ready.");
NOTREACHED();
SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
return;
}
token->RecordLog(base::StringPrintf(
"Start: %s on %s@%s %s",
local_change_.DebugString().c_str(),
url_.path().AsUTF8Unsafe().c_str(),
url_.origin().host().c_str(),
local_is_missing_ ? "(missing)" : ""));
if (local_is_missing_ && !local_change_.IsDelete()) {
// Stray file, we can just return.
token->RecordLog("Missing file for non-delete change.");
SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
return;
}
std::string app_id = url_.origin().host();
base::FilePath path = url_.path();
scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
base::FilePath active_ancestor_path;
if (!metadata_database()->FindNearestActiveAncestor(
app_id, path,
active_ancestor_tracker.get(), &active_ancestor_path)) {
// The app is disabled or not registered.
token->RecordLog("App is disabled or not registered");
SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_UNKNOWN_ORIGIN);
return;
}
DCHECK(active_ancestor_tracker->active());
DCHECK(active_ancestor_tracker->has_synced_details());
const FileDetails& active_ancestor_details =
active_ancestor_tracker->synced_details();
// TODO(tzik): Consider handling
// active_ancestor_tracker->synced_details().missing() case.
DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
base::FilePath missing_entries;
if (active_ancestor_path.empty()) {
missing_entries = path;
} else if (active_ancestor_path != path) {
if (!active_ancestor_path.AppendRelativePath(path, &missing_entries)) {
NOTREACHED();
token->RecordLog(base::StringPrintf(
"Detected invalid ancestor: %s",
active_ancestor_path.value().c_str()));
SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
return;
}
}
std::vector<base::FilePath::StringType> missing_components;
fileapi::VirtualPath::GetComponents(missing_entries, &missing_components);
if (!missing_components.empty()) {
if (local_is_missing_) {
token->RecordLog("Both local and remote are marked missing");
// !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
// deleted by recursive deletion (which is not recorded by tracker)
// but there're remaining changes for the same file in the tracker.
// Local file is deleted and remote file is missing, already deleted or
// not yet synced. There is nothing to do for the file.
SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
return;
}
}
if (missing_components.size() > 1) {
// The original target doesn't have remote file and parent.
// Try creating the parent first.
if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
target_path_ = active_ancestor_path.Append(missing_components[0]);
token->RecordLog("Detected missing parent folder.");
retry_on_success_ = true;
MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
remote_parent_folder_tracker_ =
FindTrackerByID(metadata_database(),
active_ancestor_tracker->parent_tracker_id());
remote_file_tracker_ = active_ancestor_tracker.Pass();
target_path_ = active_ancestor_path;
token->RecordLog("Detected non-folder file in its path.");
retry_on_success_ = true;
MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
if (missing_components.empty()) {
// The original target has remote active file/folder.
remote_parent_folder_tracker_ =
FindTrackerByID(metadata_database(),
active_ancestor_tracker->parent_tracker_id());
remote_file_tracker_ = active_ancestor_tracker.Pass();
target_path_ = url_.path();
DCHECK(target_path_ == active_ancestor_path);
if (remote_file_tracker_->dirty()) {
token->RecordLog(base::StringPrintf(
"Detected conflicting dirty tracker:%" PRId64,
remote_file_tracker_->tracker_id()));
// Both local and remote file has pending modification.
HandleConflict(token.Pass());
return;
}
// Non-conflicting file/folder update case.
HandleExistingRemoteFile(token.Pass());
return;
}
DCHECK(local_change_.IsAddOrUpdate());
DCHECK_EQ(1u, missing_components.size());
// The original target has remote parent folder and doesn't have remote active
// file.
// Upload the file as a new file or create a folder.
remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
target_path_ = url_.path();
DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
token->RecordLog("Detected a new file.");
MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
token->RecordLog("Detected a new folder.");
MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
}
void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
scoped_ptr<SyncTaskToken> token) {
scoped_ptr<BlockingFactor> blocker(new BlockingFactor);
blocker->app_id = url_.origin().host();
blocker->paths.push_back(target_path_);
if (remote_file_tracker_) {
if (!GetKnownChangeID(metadata_database(),
remote_file_tracker_->file_id(),
&remote_file_change_id_)) {
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
blocker->file_ids.push_back(remote_file_tracker_->file_id());
}
// Run current task as a background task with |blocker|.
// After the invocation of ContinueAsBackgroundTask
SyncTaskManager::UpdateBlockingFactor(
token.Pass(), blocker.Pass(),
base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
weak_ptr_factory_.GetWeakPtr(),
continuation));
}
void LocalToRemoteSyncer::ContinueAsBackgroundTask(
const Continuation& continuation,
scoped_ptr<SyncTaskToken> token) {
// The SyncTask runs as a background task beyond this point.
// Note that any task can run between MoveToBackground() and
// ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
// affect to the current LocalToRemoteSyncer task.
//
// - For RemoteToLocalSyncer, it doesn't actually run beyond
// PrepareForProcessRemoteChange() since it should be blocked in
// LocalFileSyncService.
// - For ListChangesTask, it may update FileMetatada together with |change_id|
// and may delete FileTracker. So, ensure |change_id| is not changed and
// check if FileTracker still exists.
// - For UninstallAppTask, it may also delete FileMetadata and FileTracker.
// And also, check if FileTracker still exists.
// - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
// LocalToRemoteSyncer.
if (remote_file_tracker_) {
int64 latest_change_id = 0;
if (!GetKnownChangeID(metadata_database(),
remote_file_tracker_->file_id(),
&latest_change_id) ||
latest_change_id > remote_file_change_id_) {
SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
return;
}
if (!metadata_database()->FindTrackerByTrackerID(
remote_file_tracker_->tracker_id(), NULL)) {
SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
return;
}
}
continuation.Run(token.Pass());
}
void LocalToRemoteSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
SyncStatusCode status) {
if (status == SYNC_STATUS_OK && retry_on_success_)
status = SYNC_STATUS_RETRY;
if (needs_remote_change_listing_)
status = SYNC_STATUS_FILE_BUSY;
token->RecordLog(base::StringPrintf(
"Finished: action=%s, status=%s for %s@%s",
SyncActionToString(sync_action_),
SyncStatusCodeToString(status),
target_path_.AsUTF8Unsafe().c_str(),
url_.origin().host().c_str()));
SyncTaskManager::NotifyTaskDone(token.Pass(), status);
}
void LocalToRemoteSyncer::HandleConflict(scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_file_tracker_);
DCHECK(remote_file_tracker_->has_synced_details());
DCHECK(remote_file_tracker_->active());
DCHECK(remote_file_tracker_->dirty());
if (local_is_missing_) {
SyncCompleted(token.Pass(), SYNC_STATUS_OK);
return;
}
if (local_change_.IsFile()) {
// Upload the conflicting file as a new file and let ConflictResolver
// resolve it.
MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
DCHECK(local_change_.IsDirectory());
// Check if we can reuse the remote folder.
FileMetadata remote_file_metadata;
if (!metadata_database()->FindFileByFileID(
remote_file_tracker_->file_id(), &remote_file_metadata)) {
NOTREACHED();
MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
const FileDetails& remote_details = remote_file_metadata.details();
base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
if (!remote_details.missing() &&
remote_details.file_kind() == FILE_KIND_FOLDER &&
remote_details.title() == title.AsUTF8Unsafe() &&
HasFileAsParent(remote_details,
remote_parent_folder_tracker_->file_id())) {
MoveToBackground(
base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
weak_ptr_factory_.GetWeakPtr(),
remote_details),
token.Pass());
return;
}
MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
}
void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
const FileDetails& details,
scoped_ptr<SyncTaskToken> token) {
metadata_database()->UpdateTracker(
remote_file_tracker_->tracker_id(), details,
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
}
void LocalToRemoteSyncer::HandleExistingRemoteFile(
scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_file_tracker_);
DCHECK(!remote_file_tracker_->dirty());
DCHECK(remote_file_tracker_->active());
DCHECK(remote_file_tracker_->has_synced_details());
if (local_is_missing_) {
// Local file deletion for existing remote file.
MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
DCHECK(local_change_.IsAddOrUpdate());
DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
const FileDetails& synced_details = remote_file_tracker_->synced_details();
DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
synced_details.file_kind() == FILE_KIND_FOLDER);
if (local_change_.IsFile()) {
if (synced_details.file_kind() == FILE_KIND_FILE) {
// Non-conflicting local file update to existing remote regular file.
MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
// Non-conflicting local file update to existing remote *folder*.
// Assuming this case as local folder deletion + local file creation, delete
// the remote folder and upload the file.
retry_on_success_ = true;
MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
DCHECK(local_change_.IsDirectory());
if (synced_details.file_kind() == FILE_KIND_FILE) {
// Non-conflicting local folder creation to existing remote *file*.
// Assuming this case as local file deletion + local folder creation, delete
// the remote file and create a remote folder.
retry_on_success_ = true;
MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
weak_ptr_factory_.GetWeakPtr()),
token.Pass());
return;
}
// Non-conflicting local folder creation to existing remote folder.
DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
SyncCompleted(token.Pass(), SYNC_STATUS_OK);
}
void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_file_tracker_);
DCHECK(remote_file_tracker_->has_synced_details());
sync_action_ = SYNC_ACTION_DELETED;
drive_service()->DeleteResource(
remote_file_tracker_->file_id(),
remote_file_tracker_->synced_details().etag(),
base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
}
void LocalToRemoteSyncer::DidDeleteRemoteFile(
scoped_ptr<SyncTaskToken> token,
google_apis::GDataErrorCode error) {
SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
if (status != SYNC_STATUS_OK &&
error != google_apis::HTTP_NOT_FOUND &&
error != google_apis::HTTP_PRECONDITION &&
error != google_apis::HTTP_CONFLICT) {
SyncCompleted(token.Pass(), status);
return;
}
// Handle NOT_FOUND case as SUCCESS case.
// For PRECONDITION / CONFLICT case, the remote file is modified since the
// last sync completed. As our policy for deletion-modification conflict
// resolution, ignore the local deletion.
if (status == SYNC_STATUS_OK ||
error == google_apis::HTTP_NOT_FOUND) {
metadata_database()->UpdateByDeletedRemoteFile(
remote_file_tracker_->file_id(),
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
return;
}
SyncCompleted(token.Pass(), SYNC_STATUS_OK);
}
void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_file_tracker_);
DCHECK(remote_file_tracker_->has_synced_details());
DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
base::Callback<void(const std::string&)> did_calculate_callback =
base::Bind(&LocalToRemoteSyncer::DidGetMD5ForUpload,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&token));
sync_context_->GetFileTaskRunner()->PostTask(
FROM_HERE,
CreateComposedFunction(
base::Bind(&drive::util::GetMd5Digest, local_path_),
RelayCallbackToTaskRunner(
sync_context_->GetWorkerTaskRunner(), FROM_HERE,
did_calculate_callback)));
}
void LocalToRemoteSyncer::DidGetMD5ForUpload(
scoped_ptr<SyncTaskToken> token,
const std::string& local_file_md5) {
if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
// Local file is not changed.
SyncCompleted(token.Pass(), SYNC_STATUS_OK);
return;
}
sync_action_ = SYNC_ACTION_UPDATED;
drive::DriveUploader::UploadExistingFileOptions options;
options.etag = remote_file_tracker_->synced_details().etag();
drive_uploader()->UploadExistingFile(
remote_file_tracker_->file_id(),
local_path_,
"application/octet_stream",
options,
base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
google_apis::ProgressCallback());
}
void LocalToRemoteSyncer::DidUploadExistingFile(
scoped_ptr<SyncTaskToken> token,
google_apis::GDataErrorCode error,
const GURL&,
scoped_ptr<google_apis::FileResource> entry) {
if (error == google_apis::HTTP_PRECONDITION ||
error == google_apis::HTTP_CONFLICT ||
error == google_apis::HTTP_NOT_FOUND) {
// The remote file has unfetched remote change. Fetch latest metadata and
// update database with it.
// TODO(tzik): Consider adding local side low-priority dirtiness handling to
// handle this as ListChangesTask.
needs_remote_change_listing_ = true;
UpdateRemoteMetadata(
remote_file_tracker_->file_id(),
token.Pass());
return;
}
SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
if (!entry) {
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
DCHECK(entry);
metadata_database()->UpdateByFileResource(
*entry,
base::Bind(&LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
}
void LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile(
scoped_ptr<SyncTaskToken> token,
SyncStatusCode status) {
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
FileMetadata file;
if (!metadata_database()->FindFileByFileID(
remote_file_tracker_->file_id(), &file)) {
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
const FileDetails& details = file.details();
base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
if (!details.missing() &&
details.file_kind() == FILE_KIND_FILE &&
details.title() == title.AsUTF8Unsafe() &&
HasFileAsParent(details,
remote_parent_folder_tracker_->file_id())) {
metadata_database()->UpdateTracker(
remote_file_tracker_->tracker_id(),
file.details(),
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
return;
}
SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
}
void LocalToRemoteSyncer::UpdateRemoteMetadata(
const std::string& file_id,
scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_file_tracker_);
drive_service()->GetFileResource(
file_id,
base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
weak_ptr_factory_.GetWeakPtr(),
file_id, base::Passed(&token)));
}
void LocalToRemoteSyncer::DidGetRemoteMetadata(
const std::string& file_id,
scoped_ptr<SyncTaskToken> token,
google_apis::GDataErrorCode error,
scoped_ptr<google_apis::FileResource> entry) {
DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
if (error == google_apis::HTTP_NOT_FOUND) {
retry_on_success_ = true;
metadata_database()->UpdateByDeletedRemoteFile(
file_id,
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
return;
}
SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
if (!entry) {
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
retry_on_success_ = true;
metadata_database()->UpdateByFileResource(
*entry,
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
}
void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_parent_folder_tracker_);
sync_action_ = SYNC_ACTION_ADDED;
base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
drive_uploader()->UploadNewFile(
remote_parent_folder_tracker_->file_id(),
local_path_,
title.AsUTF8Unsafe(),
GetMimeTypeFromTitle(title),
drive::DriveUploader::UploadNewFileOptions(),
base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)),
google_apis::ProgressCallback());
}
void LocalToRemoteSyncer::DidUploadNewFile(
scoped_ptr<SyncTaskToken> token,
google_apis::GDataErrorCode error,
const GURL& upload_location,
scoped_ptr<google_apis::FileResource> entry) {
if (error == google_apis::HTTP_NOT_FOUND)
needs_remote_change_listing_ = true;
SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
if (!entry) {
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
metadata_database()->ReplaceActiveTrackerWithNewResource(
remote_parent_folder_tracker_->tracker_id(), *entry,
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
}
void LocalToRemoteSyncer::CreateRemoteFolder(
scoped_ptr<SyncTaskToken> token) {
DCHECK(remote_parent_folder_tracker_);
base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
sync_action_ = SYNC_ACTION_ADDED;
DCHECK(!folder_creator_);
folder_creator_.reset(new FolderCreator(
drive_service(), metadata_database(),
remote_parent_folder_tracker_->file_id(),
title.AsUTF8Unsafe()));
folder_creator_->Run(base::Bind(
&LocalToRemoteSyncer::DidCreateRemoteFolder,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&token)));
}
void LocalToRemoteSyncer::DidCreateRemoteFolder(
scoped_ptr<SyncTaskToken> token,
const std::string& file_id,
SyncStatusCode status) {
if (status == SYNC_FILE_ERROR_NOT_FOUND)
needs_remote_change_listing_ = true;
scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
MetadataDatabase::ActivationStatus activation_status =
metadata_database()->TryActivateTracker(
remote_parent_folder_tracker_->tracker_id(),
file_id,
base::Bind(&LocalToRemoteSyncer::SyncCompleted,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(token.Pass())));
switch (activation_status) {
case MetadataDatabase::ACTIVATION_PENDING:
// The task will be finalized by the callback passed to MetadataDatabase
// in this case.
return;
case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
// The activation failed due to another tracker that has another parent.
// Detach the folder from the current parent to avoid using this folder as
// active folder.
drive_service()->RemoveResourceFromDirectory(
remote_parent_folder_tracker_->file_id(), file_id,
base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
return;
}
NOTREACHED();
SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
return;
}
void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
scoped_ptr<SyncTaskToken> token,
google_apis::GDataErrorCode error) {
SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
if (status != SYNC_STATUS_OK) {
SyncCompleted(token.Pass(), status);
return;
}
SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
}
bool LocalToRemoteSyncer::IsContextReady() {
return sync_context_->GetDriveService() &&
sync_context_->GetDriveUploader() &&
sync_context_->GetMetadataDatabase();
}
drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
set_used_network(true);
return sync_context_->GetDriveService();
}
drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
set_used_network(true);
return sync_context_->GetDriveUploader();
}
MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
return sync_context_->GetMetadataDatabase();
}
} // namespace drive_backend
} // namespace sync_file_system