Home | History | Annotate | Download | only in file_system
      1 // Copyright (c) 2012 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/chromeos/drive/file_system/copy_operation.h"
      6 
      7 #include <string>
      8 
      9 #include "base/task_runner_util.h"
     10 #include "chrome/browser/chromeos/drive/drive.pb.h"
     11 #include "chrome/browser/chromeos/drive/file_cache.h"
     12 #include "chrome/browser/chromeos/drive/file_change.h"
     13 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
     14 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
     15 #include "chrome/browser/chromeos/drive/file_system_util.h"
     16 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     17 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     18 #include "chrome/browser/chromeos/drive/resource_metadata.h"
     19 #include "chrome/browser/drive/drive_api_util.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "google_apis/drive/drive_api_parser.h"
     22 
     23 using content::BrowserThread;
     24 
     25 namespace drive {
     26 namespace file_system {
     27 
     28 struct CopyOperation::CopyParams {
     29   base::FilePath src_file_path;
     30   base::FilePath dest_file_path;
     31   bool preserve_last_modified;
     32   FileOperationCallback callback;
     33   ResourceEntry src_entry;
     34   ResourceEntry parent_entry;
     35 };
     36 
     37 // Enum for categorizing where a gdoc represented by a JSON file exists.
     38 enum JsonGdocLocationType {
     39   NOT_IN_METADATA,
     40   IS_ORPHAN,
     41   HAS_PARENT,
     42 };
     43 
     44 struct CopyOperation::TransferJsonGdocParams {
     45   TransferJsonGdocParams(const FileOperationCallback& callback,
     46                          const std::string& resource_id,
     47                          const ResourceEntry& parent_entry,
     48                          const std::string& new_title)
     49       : callback(callback),
     50         resource_id(resource_id),
     51         parent_resource_id(parent_entry.resource_id()),
     52         parent_local_id(parent_entry.local_id()),
     53         new_title(new_title),
     54         location_type(NOT_IN_METADATA) {
     55   }
     56   // Parameters supplied or calculated from operation arguments.
     57   const FileOperationCallback callback;
     58   const std::string resource_id;
     59   const std::string parent_resource_id;
     60   const std::string parent_local_id;
     61   const std::string new_title;
     62 
     63   // Values computed during operation.
     64   JsonGdocLocationType location_type;  // types where the gdoc file is located.
     65   std::string local_id;  // the local_id of the file (if exists in metadata.)
     66   base::FilePath changed_path;
     67 };
     68 
     69 namespace {
     70 
     71 FileError TryToCopyLocally(internal::ResourceMetadata* metadata,
     72                            internal::FileCache* cache,
     73                            CopyOperation::CopyParams* params,
     74                            std::vector<std::string>* updated_local_ids,
     75                            bool* directory_changed,
     76                            bool* should_copy_on_server) {
     77   FileError error = metadata->GetResourceEntryByPath(params->src_file_path,
     78                                                      &params->src_entry);
     79   if (error != FILE_ERROR_OK)
     80     return error;
     81 
     82   error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(),
     83                                            &params->parent_entry);
     84   if (error != FILE_ERROR_OK)
     85     return error;
     86 
     87   if (!params->parent_entry.file_info().is_directory())
     88     return FILE_ERROR_NOT_A_DIRECTORY;
     89 
     90   // Drive File System doesn't support recursive copy.
     91   if (params->src_entry.file_info().is_directory())
     92     return FILE_ERROR_NOT_A_FILE;
     93 
     94   // Check destination.
     95   ResourceEntry dest_entry;
     96   error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry);
     97   switch (error) {
     98     case FILE_ERROR_OK:
     99       // File API spec says it is an error to try to "copy a file to a path
    100       // occupied by a directory".
    101       if (dest_entry.file_info().is_directory())
    102         return FILE_ERROR_INVALID_OPERATION;
    103 
    104       // Move the existing entry to the trash.
    105       dest_entry.set_parent_local_id(util::kDriveTrashDirLocalId);
    106       error = metadata->RefreshEntry(dest_entry);
    107       if (error != FILE_ERROR_OK)
    108         return error;
    109       updated_local_ids->push_back(dest_entry.local_id());
    110       *directory_changed = true;
    111       break;
    112     case FILE_ERROR_NOT_FOUND:
    113       break;
    114     default:
    115       return error;
    116   }
    117 
    118   // If the cache file is not present and the entry exists on the server,
    119   // server side copy should be used.
    120   if (!params->src_entry.file_specific_info().cache_state().is_present() &&
    121       !params->src_entry.resource_id().empty()) {
    122     *should_copy_on_server = true;
    123     return FILE_ERROR_OK;
    124   }
    125 
    126   // Copy locally.
    127   ResourceEntry entry;
    128   const int64 now = base::Time::Now().ToInternalValue();
    129   entry.set_title(params->dest_file_path.BaseName().AsUTF8Unsafe());
    130   entry.set_parent_local_id(params->parent_entry.local_id());
    131   entry.mutable_file_specific_info()->set_content_mime_type(
    132       params->src_entry.file_specific_info().content_mime_type());
    133   entry.set_metadata_edit_state(ResourceEntry::DIRTY);
    134   entry.set_modification_date(base::Time::Now().ToInternalValue());
    135   entry.mutable_file_info()->set_last_modified(
    136       params->preserve_last_modified ?
    137       params->src_entry.file_info().last_modified() : now);
    138   entry.mutable_file_info()->set_last_accessed(now);
    139 
    140   std::string local_id;
    141   error = metadata->AddEntry(entry, &local_id);
    142   if (error != FILE_ERROR_OK)
    143     return error;
    144   updated_local_ids->push_back(local_id);
    145   *directory_changed = true;
    146 
    147   if (!params->src_entry.file_specific_info().cache_state().is_present()) {
    148     DCHECK(params->src_entry.resource_id().empty());
    149     // Locally created empty file may have no cache file.
    150     return FILE_ERROR_OK;
    151   }
    152 
    153   base::FilePath cache_file_path;
    154   error = cache->GetFile(params->src_entry.local_id(), &cache_file_path);
    155   if (error != FILE_ERROR_OK)
    156     return error;
    157 
    158   return cache->Store(local_id, std::string(), cache_file_path,
    159                       internal::FileCache::FILE_OPERATION_COPY);
    160 }
    161 
    162 // Stores the entry returned from the server and returns its path.
    163 FileError UpdateLocalStateForServerSideOperation(
    164     internal::ResourceMetadata* metadata,
    165     scoped_ptr<google_apis::FileResource> file_resource,
    166     ResourceEntry* entry,
    167     base::FilePath* file_path) {
    168   DCHECK(file_resource);
    169 
    170   std::string parent_resource_id;
    171   if (!ConvertFileResourceToResourceEntry(
    172           *file_resource, entry, &parent_resource_id) ||
    173       parent_resource_id.empty())
    174     return FILE_ERROR_NOT_A_FILE;
    175 
    176   std::string parent_local_id;
    177   FileError error = metadata->GetIdByResourceId(parent_resource_id,
    178                                                 &parent_local_id);
    179   if (error != FILE_ERROR_OK)
    180     return error;
    181   entry->set_parent_local_id(parent_local_id);
    182 
    183   std::string local_id;
    184   error = metadata->AddEntry(*entry, &local_id);
    185   // Depending on timing, the metadata may have inserted via change list
    186   // already. So, FILE_ERROR_EXISTS is not an error.
    187   if (error == FILE_ERROR_EXISTS)
    188     error = metadata->GetIdByResourceId(entry->resource_id(), &local_id);
    189 
    190   if (error != FILE_ERROR_OK)
    191     return error;
    192 
    193   return metadata->GetFilePath(local_id, file_path);
    194 }
    195 
    196 // Stores the file at |local_file_path| to the cache as a content of entry at
    197 // |remote_dest_path|, and marks it dirty.
    198 FileError UpdateLocalStateForScheduleTransfer(
    199     internal::ResourceMetadata* metadata,
    200     internal::FileCache* cache,
    201     const base::FilePath& local_src_path,
    202     const base::FilePath& remote_dest_path,
    203     ResourceEntry* entry,
    204     std::string* local_id) {
    205   FileError error = metadata->GetIdByPath(remote_dest_path, local_id);
    206   if (error != FILE_ERROR_OK)
    207     return error;
    208 
    209   error = metadata->GetResourceEntryById(*local_id, entry);
    210   if (error != FILE_ERROR_OK)
    211     return error;
    212 
    213   return cache->Store(*local_id, std::string(), local_src_path,
    214                       internal::FileCache::FILE_OPERATION_COPY);
    215 }
    216 
    217 // Gets the file size of the |local_path|, and the ResourceEntry for the parent
    218 // of |remote_path| to prepare the necessary information for transfer.
    219 FileError PrepareTransferFileFromLocalToRemote(
    220     internal::ResourceMetadata* metadata,
    221     const base::FilePath& local_src_path,
    222     const base::FilePath& remote_dest_path,
    223     std::string* gdoc_resource_id,
    224     ResourceEntry* parent_entry) {
    225   FileError error = metadata->GetResourceEntryByPath(
    226       remote_dest_path.DirName(), parent_entry);
    227   if (error != FILE_ERROR_OK)
    228     return error;
    229 
    230   // The destination's parent must be a directory.
    231   if (!parent_entry->file_info().is_directory())
    232     return FILE_ERROR_NOT_A_DIRECTORY;
    233 
    234   // Try to parse GDoc File and extract the resource id, if necessary.
    235   // Failing isn't problem. It'd be handled as a regular file, then.
    236   if (util::HasHostedDocumentExtension(local_src_path))
    237     *gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
    238   return FILE_ERROR_OK;
    239 }
    240 
    241 // Performs local work before server-side work for transferring JSON-represented
    242 // gdoc files.
    243 FileError LocalWorkForTransferJsonGdocFile(
    244     internal::ResourceMetadata* metadata,
    245     CopyOperation::TransferJsonGdocParams* params) {
    246   std::string local_id;
    247   FileError error = metadata->GetIdByResourceId(params->resource_id, &local_id);
    248   if (error != FILE_ERROR_OK) {
    249     params->location_type = NOT_IN_METADATA;
    250     return error == FILE_ERROR_NOT_FOUND ? FILE_ERROR_OK : error;
    251   }
    252 
    253   ResourceEntry entry;
    254   error = metadata->GetResourceEntryById(local_id, &entry);
    255   if (error != FILE_ERROR_OK)
    256     return error;
    257   params->local_id = entry.local_id();
    258 
    259   if (entry.parent_local_id() == util::kDriveOtherDirLocalId) {
    260     params->location_type = IS_ORPHAN;
    261     entry.set_title(params->new_title);
    262     entry.set_parent_local_id(params->parent_local_id);
    263     entry.set_metadata_edit_state(ResourceEntry::DIRTY);
    264     entry.set_modification_date(base::Time::Now().ToInternalValue());
    265     error = metadata->RefreshEntry(entry);
    266     if (error != FILE_ERROR_OK)
    267       return error;
    268     return metadata->GetFilePath(local_id, &params->changed_path);
    269   }
    270 
    271   params->location_type = HAS_PARENT;
    272   return FILE_ERROR_OK;
    273 }
    274 
    275 }  // namespace
    276 
    277 CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
    278                              OperationDelegate* delegate,
    279                              JobScheduler* scheduler,
    280                              internal::ResourceMetadata* metadata,
    281                              internal::FileCache* cache)
    282   : blocking_task_runner_(blocking_task_runner),
    283     delegate_(delegate),
    284     scheduler_(scheduler),
    285     metadata_(metadata),
    286     cache_(cache),
    287     create_file_operation_(new CreateFileOperation(blocking_task_runner,
    288                                                    delegate,
    289                                                    metadata)),
    290     weak_ptr_factory_(this) {
    291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    292 }
    293 
    294 CopyOperation::~CopyOperation() {
    295   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    296 }
    297 
    298 void CopyOperation::Copy(const base::FilePath& src_file_path,
    299                          const base::FilePath& dest_file_path,
    300                          bool preserve_last_modified,
    301                          const FileOperationCallback& callback) {
    302   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    303   DCHECK(!callback.is_null());
    304 
    305   CopyParams* params = new CopyParams;
    306   params->src_file_path = src_file_path;
    307   params->dest_file_path = dest_file_path;
    308   params->preserve_last_modified = preserve_last_modified;
    309   params->callback = callback;
    310 
    311   std::vector<std::string>* updated_local_ids = new std::vector<std::string>;
    312   bool* directory_changed = new bool(false);
    313   bool* should_copy_on_server = new bool(false);
    314   base::PostTaskAndReplyWithResult(
    315       blocking_task_runner_.get(),
    316       FROM_HERE,
    317       base::Bind(&TryToCopyLocally, metadata_, cache_, params,
    318                  updated_local_ids, directory_changed, should_copy_on_server),
    319       base::Bind(&CopyOperation::CopyAfterTryToCopyLocally,
    320                  weak_ptr_factory_.GetWeakPtr(), base::Owned(params),
    321                  base::Owned(updated_local_ids), base::Owned(directory_changed),
    322                  base::Owned(should_copy_on_server)));
    323 }
    324 
    325 void CopyOperation::CopyAfterTryToCopyLocally(
    326     const CopyParams* params,
    327     const std::vector<std::string>* updated_local_ids,
    328     const bool* directory_changed,
    329     const bool* should_copy_on_server,
    330     FileError error) {
    331   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    332   DCHECK(!params->callback.is_null());
    333 
    334   for (size_t i = 0; i < updated_local_ids->size(); ++i)
    335     delegate_->OnEntryUpdatedByOperation((*updated_local_ids)[i]);
    336 
    337   if (*directory_changed) {
    338     FileChange changed_file;
    339     DCHECK(!params->src_entry.file_info().is_directory());
    340     changed_file.Update(params->dest_file_path,
    341                         FileChange::FILE_TYPE_FILE,
    342                         FileChange::ADD_OR_UPDATE);
    343     delegate_->OnFileChangedByOperation(changed_file);
    344   }
    345 
    346   if (error != FILE_ERROR_OK || !*should_copy_on_server) {
    347     params->callback.Run(error);
    348     return;
    349   }
    350 
    351   if (params->parent_entry.resource_id().empty()) {
    352     // Parent entry may be being synced.
    353     const bool waiting = delegate_->WaitForSyncComplete(
    354         params->parent_entry.local_id(),
    355         base::Bind(&CopyOperation::CopyAfterParentSync,
    356                    weak_ptr_factory_.GetWeakPtr(), *params));
    357     if (!waiting)
    358       params->callback.Run(FILE_ERROR_NOT_FOUND);
    359   } else {
    360     CopyAfterGetParentResourceId(*params, &params->parent_entry, FILE_ERROR_OK);
    361   }
    362 }
    363 
    364 void CopyOperation::CopyAfterParentSync(const CopyParams& params,
    365                                         FileError error) {
    366   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    367   DCHECK(!params.callback.is_null());
    368 
    369   if (error != FILE_ERROR_OK) {
    370     params.callback.Run(error);
    371     return;
    372   }
    373 
    374   ResourceEntry* parent = new ResourceEntry;
    375   base::PostTaskAndReplyWithResult(
    376       blocking_task_runner_.get(),
    377       FROM_HERE,
    378       base::Bind(&internal::ResourceMetadata::GetResourceEntryById,
    379                  base::Unretained(metadata_),
    380                  params.parent_entry.local_id(),
    381                  parent),
    382       base::Bind(&CopyOperation::CopyAfterGetParentResourceId,
    383                  weak_ptr_factory_.GetWeakPtr(),
    384                  params,
    385                  base::Owned(parent)));
    386 }
    387 
    388 void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params,
    389                                                  const ResourceEntry* parent,
    390                                                  FileError error) {
    391   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    392   DCHECK(!params.callback.is_null());
    393 
    394   if (error != FILE_ERROR_OK) {
    395     params.callback.Run(error);
    396     return;
    397   }
    398 
    399   base::FilePath new_title = params.dest_file_path.BaseName();
    400   if (params.src_entry.file_specific_info().is_hosted_document()) {
    401     // Drop the document extension, which should not be in the title.
    402     // TODO(yoshiki): Remove this code with crbug.com/223304.
    403     new_title = new_title.RemoveExtension();
    404   }
    405 
    406   base::Time last_modified =
    407       params.preserve_last_modified ?
    408       base::Time::FromInternalValue(
    409           params.src_entry.file_info().last_modified()) : base::Time();
    410 
    411   CopyResourceOnServer(
    412       params.src_entry.resource_id(), parent->resource_id(),
    413       new_title.AsUTF8Unsafe(), last_modified, params.callback);
    414 }
    415 
    416 void CopyOperation::TransferFileFromLocalToRemote(
    417     const base::FilePath& local_src_path,
    418     const base::FilePath& remote_dest_path,
    419     const FileOperationCallback& callback) {
    420   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    421   DCHECK(!callback.is_null());
    422 
    423   std::string* gdoc_resource_id = new std::string;
    424   ResourceEntry* parent_entry = new ResourceEntry;
    425   base::PostTaskAndReplyWithResult(
    426       blocking_task_runner_.get(),
    427       FROM_HERE,
    428       base::Bind(
    429           &PrepareTransferFileFromLocalToRemote,
    430           metadata_, local_src_path, remote_dest_path,
    431           gdoc_resource_id, parent_entry),
    432       base::Bind(
    433           &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
    434           weak_ptr_factory_.GetWeakPtr(),
    435           local_src_path, remote_dest_path, callback,
    436           base::Owned(gdoc_resource_id), base::Owned(parent_entry)));
    437 }
    438 
    439 void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
    440     const base::FilePath& local_src_path,
    441     const base::FilePath& remote_dest_path,
    442     const FileOperationCallback& callback,
    443     std::string* gdoc_resource_id,
    444     ResourceEntry* parent_entry,
    445     FileError error) {
    446   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    447   DCHECK(!callback.is_null());
    448 
    449   if (error != FILE_ERROR_OK) {
    450     callback.Run(error);
    451     return;
    452   }
    453 
    454   // For regular files, schedule the transfer.
    455   if (gdoc_resource_id->empty()) {
    456     ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
    457     return;
    458   }
    459 
    460   // GDoc file may contain a resource ID in the old format.
    461   const std::string canonicalized_resource_id =
    462       util::CanonicalizeResourceId(*gdoc_resource_id);
    463 
    464   // Drop the document extension, which should not be in the title.
    465   // TODO(yoshiki): Remove this code with crbug.com/223304.
    466   const std::string new_title =
    467       remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe();
    468 
    469   // This is uploading a JSON file representing a hosted document.
    470   TransferJsonGdocParams* params = new TransferJsonGdocParams(
    471       callback, canonicalized_resource_id, *parent_entry, new_title);
    472   base::PostTaskAndReplyWithResult(
    473       blocking_task_runner_.get(),
    474       FROM_HERE,
    475       base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params),
    476       base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork,
    477                  weak_ptr_factory_.GetWeakPtr(), base::Owned(params)));
    478 }
    479 
    480 void CopyOperation::TransferJsonGdocFileAfterLocalWork(
    481     TransferJsonGdocParams* params,
    482     FileError error) {
    483   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    484 
    485   if (error != FILE_ERROR_OK) {
    486     params->callback.Run(error);
    487     return;
    488   }
    489 
    490   switch (params->location_type) {
    491     // When |resource_id| is found in the local metadata and it has a specific
    492     // parent folder, we assume the user's intention is to copy the document and
    493     // thus perform the server-side copy operation.
    494     case HAS_PARENT:
    495       CopyResourceOnServer(params->resource_id,
    496                            params->parent_resource_id,
    497                            params->new_title,
    498                            base::Time(),
    499                            params->callback);
    500       break;
    501     // When |resource_id| has no parent, we just set the new destination folder
    502     // as the parent, for sharing the document between the original source.
    503     // This reparenting is already done in LocalWorkForTransferJsonGdocFile().
    504     case IS_ORPHAN: {
    505       DCHECK(!params->changed_path.empty());
    506       delegate_->OnEntryUpdatedByOperation(params->local_id);
    507 
    508       FileChange changed_file;
    509       changed_file.Update(
    510           params->changed_path,
    511           FileChange::FILE_TYPE_FILE,  // This must be a hosted document.
    512           FileChange::ADD_OR_UPDATE);
    513       delegate_->OnFileChangedByOperation(changed_file);
    514       params->callback.Run(error);
    515       break;
    516     }
    517     // When the |resource_id| is not in the local metadata, assume it to be a
    518     // document just now shared on the server but not synced locally.
    519     // Same as the IS_ORPHAN case, we want to deal the case by setting parent,
    520     // but this time we need to resort to server side operation.
    521     case NOT_IN_METADATA:
    522       scheduler_->UpdateResource(
    523           params->resource_id,
    524           params->parent_resource_id,
    525           params->new_title,
    526           base::Time(),
    527           base::Time(),
    528           ClientContext(USER_INITIATED),
    529           base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
    530                      weak_ptr_factory_.GetWeakPtr(),
    531                      params->callback));
    532       break;
    533   }
    534 }
    535 
    536 void CopyOperation::CopyResourceOnServer(
    537     const std::string& resource_id,
    538     const std::string& parent_resource_id,
    539     const std::string& new_title,
    540     const base::Time& last_modified,
    541     const FileOperationCallback& callback) {
    542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    543   DCHECK(!callback.is_null());
    544 
    545   scheduler_->CopyResource(
    546       resource_id, parent_resource_id, new_title, last_modified,
    547       base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
    548                  weak_ptr_factory_.GetWeakPtr(),
    549                  callback));
    550 }
    551 
    552 void CopyOperation::UpdateAfterServerSideOperation(
    553     const FileOperationCallback& callback,
    554     google_apis::GDataErrorCode status,
    555     scoped_ptr<google_apis::FileResource> entry) {
    556   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    557   DCHECK(!callback.is_null());
    558 
    559   FileError error = GDataToFileError(status);
    560   if (error != FILE_ERROR_OK) {
    561     callback.Run(error);
    562     return;
    563   }
    564 
    565   ResourceEntry* resource_entry = new ResourceEntry;
    566 
    567   // The copy on the server side is completed successfully. Update the local
    568   // metadata.
    569   base::FilePath* file_path = new base::FilePath;
    570   base::PostTaskAndReplyWithResult(
    571       blocking_task_runner_.get(),
    572       FROM_HERE,
    573       base::Bind(&UpdateLocalStateForServerSideOperation,
    574                  metadata_,
    575                  base::Passed(&entry),
    576                  resource_entry,
    577                  file_path),
    578       base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate,
    579                  weak_ptr_factory_.GetWeakPtr(),
    580                  callback,
    581                  base::Owned(file_path),
    582                  base::Owned(resource_entry)));
    583 }
    584 
    585 void CopyOperation::UpdateAfterLocalStateUpdate(
    586     const FileOperationCallback& callback,
    587     base::FilePath* file_path,
    588     const ResourceEntry* entry,
    589     FileError error) {
    590   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    591   DCHECK(!callback.is_null());
    592 
    593   if (error == FILE_ERROR_OK) {
    594     FileChange changed_file;
    595     changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE);
    596     delegate_->OnFileChangedByOperation(changed_file);
    597   }
    598   callback.Run(error);
    599 }
    600 
    601 void CopyOperation::ScheduleTransferRegularFile(
    602     const base::FilePath& local_src_path,
    603     const base::FilePath& remote_dest_path,
    604     const FileOperationCallback& callback) {
    605   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    606   DCHECK(!callback.is_null());
    607 
    608   create_file_operation_->CreateFile(
    609       remote_dest_path,
    610       false,  // Not exclusive (OK even if a file already exists).
    611       std::string(),  // no specific mime type; CreateFile should guess it.
    612       base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
    613                  weak_ptr_factory_.GetWeakPtr(),
    614                  local_src_path, remote_dest_path, callback));
    615 }
    616 
    617 void CopyOperation::ScheduleTransferRegularFileAfterCreate(
    618     const base::FilePath& local_src_path,
    619     const base::FilePath& remote_dest_path,
    620     const FileOperationCallback& callback,
    621     FileError error) {
    622   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    623   DCHECK(!callback.is_null());
    624 
    625   if (error != FILE_ERROR_OK) {
    626     callback.Run(error);
    627     return;
    628   }
    629 
    630   std::string* local_id = new std::string;
    631   ResourceEntry* entry = new ResourceEntry;
    632   base::PostTaskAndReplyWithResult(
    633       blocking_task_runner_.get(),
    634       FROM_HERE,
    635       base::Bind(&UpdateLocalStateForScheduleTransfer,
    636                  metadata_,
    637                  cache_,
    638                  local_src_path,
    639                  remote_dest_path,
    640                  entry,
    641                  local_id),
    642       base::Bind(
    643           &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
    644           weak_ptr_factory_.GetWeakPtr(),
    645           callback,
    646           remote_dest_path,
    647           base::Owned(entry),
    648           base::Owned(local_id)));
    649 }
    650 
    651 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
    652     const FileOperationCallback& callback,
    653     const base::FilePath& remote_dest_path,
    654     const ResourceEntry* entry,
    655     std::string* local_id,
    656     FileError error) {
    657   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    658   DCHECK(!callback.is_null());
    659 
    660   if (error == FILE_ERROR_OK) {
    661     FileChange changed_file;
    662     changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE);
    663     delegate_->OnFileChangedByOperation(changed_file);
    664     delegate_->OnEntryUpdatedByOperation(*local_id);
    665   }
    666   callback.Run(error);
    667 }
    668 
    669 }  // namespace file_system
    670 }  // namespace drive
    671