Home | History | Annotate | Download | only in drive_backend
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/callback.h"
     11 #include "base/format_macros.h"
     12 #include "base/location.h"
     13 #include "base/logging.h"
     14 #include "base/sequenced_task_runner.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/task_runner_util.h"
     17 #include "chrome/browser/drive/drive_api_util.h"
     18 #include "chrome/browser/drive/drive_service_interface.h"
     19 #include "chrome/browser/drive/drive_uploader.h"
     20 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
     21 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
     22 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
     23 #include "chrome/browser/sync_file_system/drive_backend/folder_creator.h"
     24 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
     25 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
     26 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
     27 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
     28 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
     29 #include "chrome/browser/sync_file_system/logger.h"
     30 #include "google_apis/drive/drive_api_parser.h"
     31 #include "net/base/mime_util.h"
     32 #include "storage/common/fileapi/file_system_util.h"
     33 
     34 namespace sync_file_system {
     35 namespace drive_backend {
     36 
     37 namespace {
     38 
     39 scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
     40                                         int64 tracker_id) {
     41   scoped_ptr<FileTracker> tracker(new FileTracker);
     42   if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
     43     return tracker.Pass();
     44   return scoped_ptr<FileTracker>();
     45 }
     46 
     47 bool GetKnownChangeID(MetadataDatabase* metadata_database,
     48                       const std::string& file_id,
     49                       int64* change_id) {
     50   FileMetadata remote_file_metadata;
     51   if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
     52     return false;
     53   *change_id = remote_file_metadata.details().change_id();
     54   return true;
     55 }
     56 
     57 bool IsLocalFileMissing(const SyncFileMetadata& local_metadata,
     58                         const FileChange& local_change) {
     59   return local_metadata.file_type == SYNC_FILE_TYPE_UNKNOWN ||
     60          local_change.IsDelete();
     61 }
     62 
     63 std::string GetMimeTypeFromTitle(const base::FilePath& title) {
     64   base::FilePath::StringType extension = title.Extension();
     65   std::string mime_type;
     66   if (extension.empty() ||
     67       !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
     68     return kMimeTypeOctetStream;
     69   return mime_type;
     70 }
     71 
     72 }  // namespace
     73 
     74 LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
     75                                          const SyncFileMetadata& local_metadata,
     76                                          const FileChange& local_change,
     77                                          const base::FilePath& local_path,
     78                                          const storage::FileSystemURL& url)
     79     : sync_context_(sync_context),
     80       local_change_(local_change),
     81       local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
     82       local_path_(local_path),
     83       url_(url),
     84       sync_action_(SYNC_ACTION_NONE),
     85       remote_file_change_id_(0),
     86       retry_on_success_(false),
     87       needs_remote_change_listing_(false),
     88       weak_ptr_factory_(this) {
     89   DCHECK(local_is_missing_ ||
     90          local_change.file_type() == local_metadata.file_type)
     91       << local_change.DebugString() << " metadata:" << local_metadata.file_type;
     92 }
     93 
     94 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
     95 }
     96 
     97 void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
     98   token->InitializeTaskLog("Local -> Remote");
     99 
    100   if (!IsContextReady()) {
    101     token->RecordLog("Context not ready.");
    102     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
    103     return;
    104   }
    105 
    106   token->RecordLog(base::StringPrintf(
    107       "Start: %s on %s@%s %s",
    108       local_change_.DebugString().c_str(),
    109       url_.path().AsUTF8Unsafe().c_str(),
    110       url_.origin().host().c_str(),
    111       local_is_missing_ ? "(missing)" : ""));
    112 
    113   if (local_is_missing_ && !local_change_.IsDelete()) {
    114     // Stray file, we can just return.
    115     token->RecordLog("Missing file for non-delete change.");
    116     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
    117     return;
    118   }
    119 
    120   std::string app_id = url_.origin().host();
    121   base::FilePath path = url_.path();
    122 
    123   scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
    124   base::FilePath active_ancestor_path;
    125   if (!metadata_database()->FindNearestActiveAncestor(
    126           app_id, path,
    127           active_ancestor_tracker.get(), &active_ancestor_path)) {
    128     // The app is disabled or not registered.
    129     token->RecordLog("App is disabled or not registered");
    130     SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_UNKNOWN_ORIGIN);
    131     return;
    132   }
    133   DCHECK(active_ancestor_tracker->active());
    134   DCHECK(active_ancestor_tracker->has_synced_details());
    135   const FileDetails& active_ancestor_details =
    136       active_ancestor_tracker->synced_details();
    137 
    138   // TODO(tzik): Consider handling
    139   // active_ancestor_tracker->synced_details().missing() case.
    140 
    141   DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
    142          active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
    143 
    144   base::FilePath missing_entries;
    145   if (active_ancestor_path.empty()) {
    146     missing_entries = path;
    147   } else if (active_ancestor_path != path) {
    148     if (!active_ancestor_path.AppendRelativePath(path, &missing_entries)) {
    149       NOTREACHED();
    150       token->RecordLog(base::StringPrintf(
    151           "Detected invalid ancestor: %s",
    152           active_ancestor_path.value().c_str()));
    153       SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
    154       return;
    155     }
    156   }
    157 
    158   std::vector<base::FilePath::StringType> missing_components;
    159   storage::VirtualPath::GetComponents(missing_entries, &missing_components);
    160 
    161   if (!missing_components.empty()) {
    162     if (local_is_missing_) {
    163       token->RecordLog("Both local and remote are marked missing");
    164       // !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
    165       // deleted by recursive deletion (which is not recorded by tracker)
    166       // but there're remaining changes for the same file in the tracker.
    167 
    168       // Local file is deleted and remote file is missing, already deleted or
    169       // not yet synced.  There is nothing to do for the file.
    170       SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
    171       return;
    172     }
    173   }
    174 
    175   if (missing_components.size() > 1) {
    176     // The original target doesn't have remote file and parent.
    177     // Try creating the parent first.
    178     if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
    179       remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
    180       target_path_ = active_ancestor_path.Append(missing_components[0]);
    181       token->RecordLog("Detected missing parent folder.");
    182 
    183       retry_on_success_ = true;
    184       MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
    185                                   weak_ptr_factory_.GetWeakPtr()),
    186                        token.Pass());
    187       return;
    188     }
    189 
    190     DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
    191     remote_parent_folder_tracker_ =
    192         FindTrackerByID(metadata_database(),
    193                         active_ancestor_tracker->parent_tracker_id());
    194     remote_file_tracker_ = active_ancestor_tracker.Pass();
    195     target_path_ = active_ancestor_path;
    196     token->RecordLog("Detected non-folder file in its path.");
    197 
    198     retry_on_success_ = true;
    199     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
    200                                 weak_ptr_factory_.GetWeakPtr()),
    201                      token.Pass());
    202     return;
    203   }
    204 
    205   if (missing_components.empty()) {
    206     // The original target has remote active file/folder.
    207     remote_parent_folder_tracker_ =
    208         FindTrackerByID(metadata_database(),
    209                         active_ancestor_tracker->parent_tracker_id());
    210     remote_file_tracker_ = active_ancestor_tracker.Pass();
    211     target_path_ = url_.path();
    212     DCHECK(target_path_ == active_ancestor_path);
    213 
    214     if (remote_file_tracker_->dirty()) {
    215       token->RecordLog(base::StringPrintf(
    216           "Detected conflicting dirty tracker:%" PRId64,
    217            remote_file_tracker_->tracker_id()));
    218       // Both local and remote file has pending modification.
    219       HandleConflict(token.Pass());
    220       return;
    221     }
    222 
    223     // Non-conflicting file/folder update case.
    224     HandleExistingRemoteFile(token.Pass());
    225     return;
    226   }
    227 
    228   DCHECK(local_change_.IsAddOrUpdate());
    229   DCHECK_EQ(1u, missing_components.size());
    230   // The original target has remote parent folder and doesn't have remote active
    231   // file.
    232   // Upload the file as a new file or create a folder.
    233   remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
    234   target_path_ = url_.path();
    235   DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
    236   if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
    237     token->RecordLog("Detected a new file.");
    238     MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
    239                                 weak_ptr_factory_.GetWeakPtr()),
    240                      token.Pass());
    241     return;
    242   }
    243 
    244   token->RecordLog("Detected a new folder.");
    245   MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
    246                               weak_ptr_factory_.GetWeakPtr()),
    247                    token.Pass());
    248 }
    249 
    250 void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
    251                                            scoped_ptr<SyncTaskToken> token) {
    252   scoped_ptr<TaskBlocker> blocker(new TaskBlocker);
    253   blocker->app_id = url_.origin().host();
    254   blocker->paths.push_back(target_path_);
    255 
    256   if (remote_file_tracker_) {
    257     if (!GetKnownChangeID(metadata_database(),
    258                           remote_file_tracker_->file_id(),
    259                           &remote_file_change_id_)) {
    260       NOTREACHED();
    261       SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    262       return;
    263     }
    264 
    265     blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
    266     blocker->file_ids.push_back(remote_file_tracker_->file_id());
    267   }
    268 
    269   // Run current task as a background task with |blocker|.
    270   // After the invocation of ContinueAsBackgroundTask
    271   SyncTaskManager::UpdateTaskBlocker(
    272       token.Pass(), blocker.Pass(),
    273       base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
    274                  weak_ptr_factory_.GetWeakPtr(),
    275                  continuation));
    276 }
    277 
    278 void LocalToRemoteSyncer::ContinueAsBackgroundTask(
    279     const Continuation& continuation,
    280     scoped_ptr<SyncTaskToken> token) {
    281   // The SyncTask runs as a background task beyond this point.
    282   // Note that any task can run between MoveToBackground() and
    283   // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
    284   // affect to the current LocalToRemoteSyncer task.
    285   //
    286   // - For RemoteToLocalSyncer, it doesn't actually run beyond
    287   //   PrepareForProcessRemoteChange() since it should be blocked in
    288   //   LocalFileSyncService.
    289   // - For ListChangesTask, it may update FileMetatada together with |change_id|
    290   //   and may delete FileTracker.  So, ensure |change_id| is not changed and
    291   //   check if FileTracker still exists.
    292   // - For UninstallAppTask, it may also delete FileMetadata and FileTracker.
    293   //   Check if FileTracker still exists.
    294   // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
    295   //   LocalToRemoteSyncer.
    296   if (remote_file_tracker_) {
    297     int64 latest_change_id = 0;
    298     if (!GetKnownChangeID(metadata_database(),
    299                           remote_file_tracker_->file_id(),
    300                           &latest_change_id) ||
    301         latest_change_id > remote_file_change_id_) {
    302       SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
    303       return;
    304     }
    305 
    306     if (!metadata_database()->FindTrackerByTrackerID(
    307             remote_file_tracker_->tracker_id(), NULL)) {
    308       SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
    309       return;
    310     }
    311   }
    312 
    313   continuation.Run(token.Pass());
    314 }
    315 
    316 void LocalToRemoteSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
    317                                         SyncStatusCode status) {
    318   if (status == SYNC_STATUS_OK && retry_on_success_)
    319     status = SYNC_STATUS_RETRY;
    320 
    321   if (needs_remote_change_listing_)
    322     status = SYNC_STATUS_FILE_BUSY;
    323 
    324   token->RecordLog(base::StringPrintf(
    325       "Finished: action=%s, status=%s for %s@%s",
    326       SyncActionToString(sync_action_),
    327       SyncStatusCodeToString(status),
    328       target_path_.AsUTF8Unsafe().c_str(),
    329       url_.origin().host().c_str()));
    330 
    331   SyncTaskManager::NotifyTaskDone(token.Pass(), status);
    332 }
    333 
    334 void LocalToRemoteSyncer::HandleConflict(scoped_ptr<SyncTaskToken> token) {
    335   DCHECK(remote_file_tracker_);
    336   DCHECK(remote_file_tracker_->has_synced_details());
    337   DCHECK(remote_file_tracker_->active());
    338   DCHECK(remote_file_tracker_->dirty());
    339 
    340   if (local_is_missing_) {
    341     SyncCompleted(token.Pass(), SYNC_STATUS_OK);
    342     return;
    343   }
    344 
    345   if (local_change_.IsFile()) {
    346     // Upload the conflicting file as a new file and let ConflictResolver
    347     // resolve it.
    348     MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
    349                                 weak_ptr_factory_.GetWeakPtr()),
    350                      token.Pass());
    351     return;
    352   }
    353 
    354   DCHECK(local_change_.IsDirectory());
    355   // Check if we can reuse the remote folder.
    356   FileMetadata remote_file_metadata;
    357   if (!metadata_database()->FindFileByFileID(
    358           remote_file_tracker_->file_id(), &remote_file_metadata)) {
    359     NOTREACHED();
    360     MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
    361                                 weak_ptr_factory_.GetWeakPtr()),
    362                      token.Pass());
    363     return;
    364   }
    365 
    366   const FileDetails& remote_details = remote_file_metadata.details();
    367   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
    368   if (!remote_details.missing() &&
    369       remote_details.file_kind() == FILE_KIND_FOLDER &&
    370       remote_details.title() == title.AsUTF8Unsafe() &&
    371       HasFileAsParent(remote_details,
    372                       remote_parent_folder_tracker_->file_id())) {
    373     MoveToBackground(
    374         base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
    375                    weak_ptr_factory_.GetWeakPtr(),
    376                    remote_details),
    377         token.Pass());
    378     return;
    379   }
    380 
    381   MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
    382                               weak_ptr_factory_.GetWeakPtr()),
    383                    token.Pass());
    384 }
    385 
    386 void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
    387     const FileDetails& details,
    388     scoped_ptr<SyncTaskToken> token) {
    389   SyncStatusCode status = metadata_database()->UpdateTracker(
    390       remote_file_tracker_->tracker_id(), details);
    391   SyncCompleted(token.Pass(), status);
    392 }
    393 
    394 void LocalToRemoteSyncer::HandleExistingRemoteFile(
    395     scoped_ptr<SyncTaskToken> token) {
    396   DCHECK(remote_file_tracker_);
    397   DCHECK(!remote_file_tracker_->dirty());
    398   DCHECK(remote_file_tracker_->active());
    399   DCHECK(remote_file_tracker_->has_synced_details());
    400 
    401   if (local_is_missing_) {
    402     // Local file deletion for existing remote file.
    403     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
    404                                 weak_ptr_factory_.GetWeakPtr()),
    405                      token.Pass());
    406     return;
    407   }
    408 
    409   DCHECK(local_change_.IsAddOrUpdate());
    410   DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
    411 
    412   const FileDetails& synced_details = remote_file_tracker_->synced_details();
    413   DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
    414          synced_details.file_kind() == FILE_KIND_FOLDER);
    415   if (local_change_.IsFile()) {
    416     if (synced_details.file_kind() == FILE_KIND_FILE) {
    417       // Non-conflicting local file update to existing remote regular file.
    418       MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
    419                                   weak_ptr_factory_.GetWeakPtr()),
    420                        token.Pass());
    421       return;
    422     }
    423 
    424     DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
    425     // Non-conflicting local file update to existing remote *folder*.
    426     // Assuming this case as local folder deletion + local file creation, delete
    427     // the remote folder and upload the file.
    428     retry_on_success_ = true;
    429     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
    430                                 weak_ptr_factory_.GetWeakPtr()),
    431                      token.Pass());
    432     return;
    433   }
    434 
    435   DCHECK(local_change_.IsDirectory());
    436   if (synced_details.file_kind() == FILE_KIND_FILE) {
    437     // Non-conflicting local folder creation to existing remote *file*.
    438     // Assuming this case as local file deletion + local folder creation, delete
    439     // the remote file and create a remote folder.
    440     retry_on_success_ = true;
    441     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
    442                                 weak_ptr_factory_.GetWeakPtr()),
    443                      token.Pass());
    444     return;
    445   }
    446 
    447   // Non-conflicting local folder creation to existing remote folder.
    448   DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
    449   SyncCompleted(token.Pass(), SYNC_STATUS_OK);
    450 }
    451 
    452 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
    453   DCHECK(remote_file_tracker_);
    454   DCHECK(remote_file_tracker_->has_synced_details());
    455 
    456   sync_action_ = SYNC_ACTION_DELETED;
    457   drive_service()->DeleteResource(
    458       remote_file_tracker_->file_id(),
    459       remote_file_tracker_->synced_details().etag(),
    460       base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
    461                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
    462 }
    463 
    464 void LocalToRemoteSyncer::DidDeleteRemoteFile(
    465     scoped_ptr<SyncTaskToken> token,
    466     google_apis::GDataErrorCode error) {
    467   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    468   if (status != SYNC_STATUS_OK &&
    469       error != google_apis::HTTP_NOT_FOUND &&
    470       error != google_apis::HTTP_PRECONDITION &&
    471       error != google_apis::HTTP_CONFLICT) {
    472     SyncCompleted(token.Pass(), status);
    473     return;
    474   }
    475 
    476   // Handle NOT_FOUND case as SUCCESS case.
    477   // For PRECONDITION / CONFLICT case, the remote file is modified since the
    478   // last sync completed.  As our policy for deletion-modification conflict
    479   // resolution, ignore the local deletion.
    480   if (status == SYNC_STATUS_OK ||
    481       error == google_apis::HTTP_NOT_FOUND) {
    482     SyncStatusCode status = metadata_database()->UpdateByDeletedRemoteFile(
    483         remote_file_tracker_->file_id());
    484     SyncCompleted(token.Pass(), status);
    485     return;
    486   }
    487 
    488   SyncCompleted(token.Pass(), SYNC_STATUS_OK);
    489 }
    490 
    491 void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
    492   DCHECK(remote_file_tracker_);
    493   DCHECK(remote_file_tracker_->has_synced_details());
    494   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
    495 
    496   const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_);
    497   if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
    498     // Local file is not changed.
    499     SyncCompleted(token.Pass(), SYNC_STATUS_OK);
    500     return;
    501   }
    502 
    503   sync_action_ = SYNC_ACTION_UPDATED;
    504 
    505   drive::DriveUploader::UploadExistingFileOptions options;
    506   options.etag = remote_file_tracker_->synced_details().etag();
    507   drive_uploader()->UploadExistingFile(
    508       remote_file_tracker_->file_id(),
    509       local_path_,
    510       "application/octet_stream",
    511       options,
    512       base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
    513                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
    514       google_apis::ProgressCallback());
    515 }
    516 
    517 void LocalToRemoteSyncer::DidUploadExistingFile(
    518     scoped_ptr<SyncTaskToken> token,
    519     google_apis::GDataErrorCode error,
    520     const GURL&,
    521     scoped_ptr<google_apis::FileResource> entry) {
    522   if (error == google_apis::HTTP_PRECONDITION ||
    523       error == google_apis::HTTP_CONFLICT ||
    524       error == google_apis::HTTP_NOT_FOUND) {
    525     // The remote file has unfetched remote change.  Fetch latest metadata and
    526     // update database with it.
    527     // TODO(tzik): Consider adding local side low-priority dirtiness handling to
    528     // handle this as ListChangesTask.
    529 
    530     needs_remote_change_listing_ = true;
    531     UpdateRemoteMetadata(
    532         remote_file_tracker_->file_id(),
    533         token.Pass());
    534     return;
    535   }
    536 
    537   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    538   if (status != SYNC_STATUS_OK) {
    539     SyncCompleted(token.Pass(), status);
    540     return;
    541   }
    542 
    543   if (!entry) {
    544     NOTREACHED();
    545     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    546     return;
    547   }
    548 
    549   DCHECK(entry);
    550   status = metadata_database()->UpdateByFileResource(*entry);
    551   if (status != SYNC_STATUS_OK) {
    552     SyncCompleted(token.Pass(), status);
    553     return;
    554   }
    555 
    556   FileMetadata file;
    557   if (!metadata_database()->FindFileByFileID(
    558           remote_file_tracker_->file_id(), &file)) {
    559     NOTREACHED();
    560     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    561     return;
    562   }
    563 
    564   const FileDetails& details = file.details();
    565   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
    566   if (!details.missing() &&
    567       details.file_kind() == FILE_KIND_FILE &&
    568       details.title() == title.AsUTF8Unsafe() &&
    569       HasFileAsParent(details,
    570                       remote_parent_folder_tracker_->file_id())) {
    571     SyncStatusCode status = metadata_database()->UpdateTracker(
    572         remote_file_tracker_->tracker_id(), file.details());
    573     SyncCompleted(token.Pass(), status);
    574     return;
    575   }
    576 
    577   SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
    578 }
    579 
    580 void LocalToRemoteSyncer::UpdateRemoteMetadata(
    581     const std::string& file_id,
    582     scoped_ptr<SyncTaskToken> token) {
    583   DCHECK(remote_file_tracker_);
    584 
    585   drive_service()->GetFileResource(
    586       file_id,
    587       base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
    588                  weak_ptr_factory_.GetWeakPtr(),
    589                  file_id, base::Passed(&token)));
    590 }
    591 
    592 void LocalToRemoteSyncer::DidGetRemoteMetadata(
    593     const std::string& file_id,
    594     scoped_ptr<SyncTaskToken> token,
    595     google_apis::GDataErrorCode error,
    596     scoped_ptr<google_apis::FileResource> entry) {
    597   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
    598 
    599   if (error == google_apis::HTTP_NOT_FOUND) {
    600     retry_on_success_ = true;
    601     SyncStatusCode status =
    602         metadata_database()->UpdateByDeletedRemoteFile(file_id);
    603     SyncCompleted(token.Pass(), status);
    604     return;
    605   }
    606 
    607   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    608   if (status != SYNC_STATUS_OK) {
    609     SyncCompleted(token.Pass(), status);
    610     return;
    611   }
    612 
    613   if (!entry) {
    614     NOTREACHED();
    615     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    616     return;
    617   }
    618 
    619   retry_on_success_ = true;
    620   status = metadata_database()->UpdateByFileResource(*entry);
    621   SyncCompleted(token.Pass(), status);
    622 }
    623 
    624 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
    625   DCHECK(remote_parent_folder_tracker_);
    626 
    627   sync_action_ = SYNC_ACTION_ADDED;
    628   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
    629   drive_uploader()->UploadNewFile(
    630       remote_parent_folder_tracker_->file_id(),
    631       local_path_,
    632       title.AsUTF8Unsafe(),
    633       GetMimeTypeFromTitle(title),
    634       drive::DriveUploader::UploadNewFileOptions(),
    635       base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
    636                  weak_ptr_factory_.GetWeakPtr(),
    637                  base::Passed(&token)),
    638       google_apis::ProgressCallback());
    639 }
    640 
    641 void LocalToRemoteSyncer::DidUploadNewFile(
    642     scoped_ptr<SyncTaskToken> token,
    643     google_apis::GDataErrorCode error,
    644     const GURL& upload_location,
    645     scoped_ptr<google_apis::FileResource> entry) {
    646   if (error == google_apis::HTTP_NOT_FOUND)
    647     needs_remote_change_listing_ = true;
    648 
    649   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    650   if (status != SYNC_STATUS_OK) {
    651     SyncCompleted(token.Pass(), status);
    652     return;
    653   }
    654 
    655   if (!entry) {
    656     NOTREACHED();
    657     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    658     return;
    659   }
    660 
    661   status = metadata_database()->ReplaceActiveTrackerWithNewResource(
    662       remote_parent_folder_tracker_->tracker_id(), *entry);
    663   SyncCompleted(token.Pass(), status);
    664 }
    665 
    666 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) {
    667   DCHECK(remote_parent_folder_tracker_);
    668 
    669   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
    670   sync_action_ = SYNC_ACTION_ADDED;
    671 
    672   DCHECK(!folder_creator_);
    673   folder_creator_.reset(new FolderCreator(
    674       drive_service(), metadata_database(),
    675       remote_parent_folder_tracker_->file_id(),
    676       title.AsUTF8Unsafe()));
    677   folder_creator_->Run(base::Bind(
    678       &LocalToRemoteSyncer::DidCreateRemoteFolder,
    679       weak_ptr_factory_.GetWeakPtr(),
    680       base::Passed(&token)));
    681 }
    682 
    683 void LocalToRemoteSyncer::DidCreateRemoteFolder(
    684     scoped_ptr<SyncTaskToken> token,
    685     const std::string& file_id,
    686     SyncStatusCode status) {
    687   if (status == SYNC_FILE_ERROR_NOT_FOUND)
    688     needs_remote_change_listing_ = true;
    689 
    690   scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
    691   if (status != SYNC_STATUS_OK) {
    692     SyncCompleted(token.Pass(), status);
    693     return;
    694   }
    695 
    696   status = SYNC_STATUS_FAILED;
    697   MetadataDatabase::ActivationStatus activation_status =
    698       metadata_database()->TryActivateTracker(
    699           remote_parent_folder_tracker_->tracker_id(),
    700           file_id, &status);
    701   switch (activation_status) {
    702     case MetadataDatabase::ACTIVATION_PENDING:
    703       SyncCompleted(token.Pass(), status);
    704       return;
    705     case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
    706       // The activation failed due to another tracker that has another parent.
    707       // Detach the folder from the current parent to avoid using this folder as
    708       // active folder.
    709       drive_service()->RemoveResourceFromDirectory(
    710           remote_parent_folder_tracker_->file_id(), file_id,
    711           base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
    712                      weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
    713       return;
    714   }
    715 
    716   NOTREACHED();
    717   SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
    718   return;
    719 }
    720 
    721 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
    722     scoped_ptr<SyncTaskToken> token,
    723     google_apis::GDataErrorCode error) {
    724   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    725   if (status != SYNC_STATUS_OK) {
    726     SyncCompleted(token.Pass(), status);
    727     return;
    728   }
    729 
    730   SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
    731 }
    732 
    733 bool LocalToRemoteSyncer::IsContextReady() {
    734   return sync_context_->GetDriveService() &&
    735       sync_context_->GetDriveUploader() &&
    736       sync_context_->GetMetadataDatabase();
    737 }
    738 
    739 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
    740   set_used_network(true);
    741   return sync_context_->GetDriveService();
    742 }
    743 
    744 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
    745   set_used_network(true);
    746   return sync_context_->GetDriveUploader();
    747 }
    748 
    749 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
    750   return sync_context_->GetMetadataDatabase();
    751 }
    752 
    753 }  // namespace drive_backend
    754 }  // namespace sync_file_system
    755