Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system / copy_operation.cc
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_,
377       FROM_HERE,
378       base::Bind(&internal::ResourceMetadata::GetResourceEntryById,
379                  base::Unretained(metadata_),
380                  params.parent_entry.local_id(), parent),
381       base::Bind(&CopyOperation::CopyAfterGetParentResourceId,
382                  weak_ptr_factory_.GetWeakPtr(), params, base::Owned(parent)));
383 }
384
385 void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params,
386                                                  const ResourceEntry* parent,
387                                                  FileError error) {
388   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389   DCHECK(!params.callback.is_null());
390
391   if (error != FILE_ERROR_OK) {
392     params.callback.Run(error);
393     return;
394   }
395
396   base::FilePath new_title = params.dest_file_path.BaseName();
397   if (params.src_entry.file_specific_info().is_hosted_document()) {
398     // Drop the document extension, which should not be in the title.
399     // TODO(yoshiki): Remove this code with crbug.com/223304.
400     new_title = new_title.RemoveExtension();
401   }
402
403   base::Time last_modified =
404       params.preserve_last_modified ?
405       base::Time::FromInternalValue(
406           params.src_entry.file_info().last_modified()) : base::Time();
407
408   CopyResourceOnServer(
409       params.src_entry.resource_id(), parent->resource_id(),
410       new_title.AsUTF8Unsafe(), last_modified, params.callback);
411 }
412
413 void CopyOperation::TransferFileFromLocalToRemote(
414     const base::FilePath& local_src_path,
415     const base::FilePath& remote_dest_path,
416     const FileOperationCallback& callback) {
417   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418   DCHECK(!callback.is_null());
419
420   std::string* gdoc_resource_id = new std::string;
421   ResourceEntry* parent_entry = new ResourceEntry;
422   base::PostTaskAndReplyWithResult(
423       blocking_task_runner_.get(),
424       FROM_HERE,
425       base::Bind(
426           &PrepareTransferFileFromLocalToRemote,
427           metadata_, local_src_path, remote_dest_path,
428           gdoc_resource_id, parent_entry),
429       base::Bind(
430           &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
431           weak_ptr_factory_.GetWeakPtr(),
432           local_src_path, remote_dest_path, callback,
433           base::Owned(gdoc_resource_id), base::Owned(parent_entry)));
434 }
435
436 void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
437     const base::FilePath& local_src_path,
438     const base::FilePath& remote_dest_path,
439     const FileOperationCallback& callback,
440     std::string* gdoc_resource_id,
441     ResourceEntry* parent_entry,
442     FileError error) {
443   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
444   DCHECK(!callback.is_null());
445
446   if (error != FILE_ERROR_OK) {
447     callback.Run(error);
448     return;
449   }
450
451   // For regular files, schedule the transfer.
452   if (gdoc_resource_id->empty()) {
453     ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
454     return;
455   }
456
457   // GDoc file may contain a resource ID in the old format.
458   const std::string canonicalized_resource_id =
459       util::CanonicalizeResourceId(*gdoc_resource_id);
460
461   // Drop the document extension, which should not be in the title.
462   // TODO(yoshiki): Remove this code with crbug.com/223304.
463   const std::string new_title =
464       remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe();
465
466   // This is uploading a JSON file representing a hosted document.
467   TransferJsonGdocParams* params = new TransferJsonGdocParams(
468       callback, canonicalized_resource_id, *parent_entry, new_title);
469   base::PostTaskAndReplyWithResult(
470       blocking_task_runner_.get(),
471       FROM_HERE,
472       base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params),
473       base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork,
474                  weak_ptr_factory_.GetWeakPtr(), base::Owned(params)));
475 }
476
477 void CopyOperation::TransferJsonGdocFileAfterLocalWork(
478     TransferJsonGdocParams* params,
479     FileError error) {
480   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481
482   if (error != FILE_ERROR_OK) {
483     params->callback.Run(error);
484     return;
485   }
486
487   switch (params->location_type) {
488     // When |resource_id| is found in the local metadata and it has a specific
489     // parent folder, we assume the user's intention is to copy the document and
490     // thus perform the server-side copy operation.
491     case HAS_PARENT:
492       CopyResourceOnServer(params->resource_id,
493                            params->parent_resource_id,
494                            params->new_title,
495                            base::Time(),
496                            params->callback);
497       break;
498     // When |resource_id| has no parent, we just set the new destination folder
499     // as the parent, for sharing the document between the original source.
500     // This reparenting is already done in LocalWorkForTransferJsonGdocFile().
501     case IS_ORPHAN: {
502       DCHECK(!params->changed_path.empty());
503       delegate_->OnEntryUpdatedByOperation(params->local_id);
504
505       FileChange changed_file;
506       changed_file.Update(
507           params->changed_path,
508           FileChange::FILE_TYPE_FILE,  // This must be a hosted document.
509           FileChange::ADD_OR_UPDATE);
510       delegate_->OnFileChangedByOperation(changed_file);
511       params->callback.Run(error);
512       break;
513     }
514     // When the |resource_id| is not in the local metadata, assume it to be a
515     // document just now shared on the server but not synced locally.
516     // Same as the IS_ORPHAN case, we want to deal the case by setting parent,
517     // but this time we need to resort to server side operation.
518     case NOT_IN_METADATA:
519       scheduler_->UpdateResource(
520           params->resource_id,
521           params->parent_resource_id,
522           params->new_title,
523           base::Time(),
524           base::Time(),
525           ClientContext(USER_INITIATED),
526           base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
527                      weak_ptr_factory_.GetWeakPtr(),
528                      params->callback));
529       break;
530   }
531 }
532
533 void CopyOperation::CopyResourceOnServer(
534     const std::string& resource_id,
535     const std::string& parent_resource_id,
536     const std::string& new_title,
537     const base::Time& last_modified,
538     const FileOperationCallback& callback) {
539   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
540   DCHECK(!callback.is_null());
541
542   scheduler_->CopyResource(
543       resource_id, parent_resource_id, new_title, last_modified,
544       base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
545                  weak_ptr_factory_.GetWeakPtr(),
546                  callback));
547 }
548
549 void CopyOperation::UpdateAfterServerSideOperation(
550     const FileOperationCallback& callback,
551     google_apis::GDataErrorCode status,
552     scoped_ptr<google_apis::FileResource> entry) {
553   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
554   DCHECK(!callback.is_null());
555
556   FileError error = GDataToFileError(status);
557   if (error != FILE_ERROR_OK) {
558     callback.Run(error);
559     return;
560   }
561
562   ResourceEntry* resource_entry = new ResourceEntry;
563
564   // The copy on the server side is completed successfully. Update the local
565   // metadata.
566   base::FilePath* file_path = new base::FilePath;
567   base::PostTaskAndReplyWithResult(
568       blocking_task_runner_.get(),
569       FROM_HERE,
570       base::Bind(&UpdateLocalStateForServerSideOperation,
571                  metadata_,
572                  base::Passed(&entry),
573                  resource_entry,
574                  file_path),
575       base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate,
576                  weak_ptr_factory_.GetWeakPtr(),
577                  callback,
578                  base::Owned(file_path),
579                  base::Owned(resource_entry)));
580 }
581
582 void CopyOperation::UpdateAfterLocalStateUpdate(
583     const FileOperationCallback& callback,
584     base::FilePath* file_path,
585     const ResourceEntry* entry,
586     FileError error) {
587   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
588   DCHECK(!callback.is_null());
589
590   if (error == FILE_ERROR_OK) {
591     FileChange changed_file;
592     changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE);
593     delegate_->OnFileChangedByOperation(changed_file);
594   }
595   callback.Run(error);
596 }
597
598 void CopyOperation::ScheduleTransferRegularFile(
599     const base::FilePath& local_src_path,
600     const base::FilePath& remote_dest_path,
601     const FileOperationCallback& callback) {
602   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
603   DCHECK(!callback.is_null());
604
605   create_file_operation_->CreateFile(
606       remote_dest_path,
607       false,  // Not exclusive (OK even if a file already exists).
608       std::string(),  // no specific mime type; CreateFile should guess it.
609       base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
610                  weak_ptr_factory_.GetWeakPtr(),
611                  local_src_path, remote_dest_path, callback));
612 }
613
614 void CopyOperation::ScheduleTransferRegularFileAfterCreate(
615     const base::FilePath& local_src_path,
616     const base::FilePath& remote_dest_path,
617     const FileOperationCallback& callback,
618     FileError error) {
619   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
620   DCHECK(!callback.is_null());
621
622   if (error != FILE_ERROR_OK) {
623     callback.Run(error);
624     return;
625   }
626
627   std::string* local_id = new std::string;
628   ResourceEntry* entry = new ResourceEntry;
629   base::PostTaskAndReplyWithResult(
630       blocking_task_runner_.get(),
631       FROM_HERE,
632       base::Bind(&UpdateLocalStateForScheduleTransfer,
633                  metadata_,
634                  cache_,
635                  local_src_path,
636                  remote_dest_path,
637                  entry,
638                  local_id),
639       base::Bind(
640           &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
641           weak_ptr_factory_.GetWeakPtr(),
642           callback,
643           remote_dest_path,
644           base::Owned(entry),
645           base::Owned(local_id)));
646 }
647
648 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
649     const FileOperationCallback& callback,
650     const base::FilePath& remote_dest_path,
651     const ResourceEntry* entry,
652     std::string* local_id,
653     FileError error) {
654   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655   DCHECK(!callback.is_null());
656
657   if (error == FILE_ERROR_OK) {
658     FileChange changed_file;
659     changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE);
660     delegate_->OnFileChangedByOperation(changed_file);
661     delegate_->OnEntryUpdatedByOperation(*local_id);
662   }
663   callback.Run(error);
664 }
665
666 }  // namespace file_system
667 }  // namespace drive