1 // Copyright (c) 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 "storage/browser/fileapi/copy_or_move_operation_delegate.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11 #include "storage/browser/blob/file_stream_reader.h"
12 #include "storage/browser/fileapi/copy_or_move_file_validator.h"
13 #include "storage/browser/fileapi/file_observers.h"
14 #include "storage/browser/fileapi/file_stream_writer.h"
15 #include "storage/browser/fileapi/file_system_context.h"
16 #include "storage/browser/fileapi/file_system_operation_runner.h"
17 #include "storage/browser/fileapi/file_system_url.h"
18 #include "storage/browser/fileapi/recursive_operation_delegate.h"
19 #include "storage/common/blob/shareable_file_reference.h"
20 #include "storage/common/fileapi/file_system_util.h"
21
22 namespace storage {
23
24 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
25
26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
27 public:
~CopyOrMoveImpl()28 virtual ~CopyOrMoveImpl() {}
29 virtual void Run(
30 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
31 virtual void Cancel() = 0;
32
33 protected:
CopyOrMoveImpl()34 CopyOrMoveImpl() {}
35
36 private:
37 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
38 };
39
40 namespace {
41
42 // Copies a file on a (same) file system. Just delegate the operation to
43 // |operation_runner|.
44 class CopyOrMoveOnSameFileSystemImpl
45 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
46 public:
CopyOrMoveOnSameFileSystemImpl(FileSystemOperationRunner * operation_runner,CopyOrMoveOperationDelegate::OperationType operation_type,const FileSystemURL & src_url,const FileSystemURL & dest_url,CopyOrMoveOperationDelegate::CopyOrMoveOption option,const FileSystemOperation::CopyFileProgressCallback & file_progress_callback)47 CopyOrMoveOnSameFileSystemImpl(
48 FileSystemOperationRunner* operation_runner,
49 CopyOrMoveOperationDelegate::OperationType operation_type,
50 const FileSystemURL& src_url,
51 const FileSystemURL& dest_url,
52 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
53 const FileSystemOperation::CopyFileProgressCallback&
54 file_progress_callback)
55 : operation_runner_(operation_runner),
56 operation_type_(operation_type),
57 src_url_(src_url),
58 dest_url_(dest_url),
59 option_(option),
60 file_progress_callback_(file_progress_callback) {
61 }
62
Run(const CopyOrMoveOperationDelegate::StatusCallback & callback)63 virtual void Run(
64 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
65 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
66 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
67 } else {
68 operation_runner_->CopyFileLocal(
69 src_url_, dest_url_, option_, file_progress_callback_, callback);
70 }
71 }
72
Cancel()73 virtual void Cancel() OVERRIDE {
74 // We can do nothing for the copy/move operation on a local file system.
75 // Assuming the operation is quickly done, it should be ok to just wait
76 // for the completion.
77 }
78
79 private:
80 FileSystemOperationRunner* operation_runner_;
81 CopyOrMoveOperationDelegate::OperationType operation_type_;
82 FileSystemURL src_url_;
83 FileSystemURL dest_url_;
84 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
85 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
86 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
87 };
88
89 // Specifically for cross file system copy/move operation, this class creates
90 // a snapshot file, validates it if necessary, runs copying process,
91 // validates the created file, and removes source file for move (noop for
92 // copy).
93 class SnapshotCopyOrMoveImpl
94 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
95 public:
SnapshotCopyOrMoveImpl(FileSystemOperationRunner * operation_runner,CopyOrMoveOperationDelegate::OperationType operation_type,const FileSystemURL & src_url,const FileSystemURL & dest_url,CopyOrMoveOperationDelegate::CopyOrMoveOption option,CopyOrMoveFileValidatorFactory * validator_factory,const FileSystemOperation::CopyFileProgressCallback & file_progress_callback)96 SnapshotCopyOrMoveImpl(
97 FileSystemOperationRunner* operation_runner,
98 CopyOrMoveOperationDelegate::OperationType operation_type,
99 const FileSystemURL& src_url,
100 const FileSystemURL& dest_url,
101 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
102 CopyOrMoveFileValidatorFactory* validator_factory,
103 const FileSystemOperation::CopyFileProgressCallback&
104 file_progress_callback)
105 : operation_runner_(operation_runner),
106 operation_type_(operation_type),
107 src_url_(src_url),
108 dest_url_(dest_url),
109 option_(option),
110 validator_factory_(validator_factory),
111 file_progress_callback_(file_progress_callback),
112 cancel_requested_(false),
113 weak_factory_(this) {
114 }
115
Run(const CopyOrMoveOperationDelegate::StatusCallback & callback)116 virtual void Run(
117 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
118 file_progress_callback_.Run(0);
119 operation_runner_->CreateSnapshotFile(
120 src_url_,
121 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
122 weak_factory_.GetWeakPtr(), callback));
123 }
124
Cancel()125 virtual void Cancel() OVERRIDE {
126 cancel_requested_ = true;
127 }
128
129 private:
RunAfterCreateSnapshot(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error,const base::File::Info & file_info,const base::FilePath & platform_path,const scoped_refptr<storage::ShareableFileReference> & file_ref)130 void RunAfterCreateSnapshot(
131 const CopyOrMoveOperationDelegate::StatusCallback& callback,
132 base::File::Error error,
133 const base::File::Info& file_info,
134 const base::FilePath& platform_path,
135 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
136 if (cancel_requested_)
137 error = base::File::FILE_ERROR_ABORT;
138
139 if (error != base::File::FILE_OK) {
140 callback.Run(error);
141 return;
142 }
143
144 // For now we assume CreateSnapshotFile always return a valid local file
145 // path.
146 DCHECK(!platform_path.empty());
147
148 if (!validator_factory_) {
149 // No validation is needed.
150 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
151 base::File::FILE_OK);
152 return;
153 }
154
155 // Run pre write validation.
156 PreWriteValidation(
157 platform_path,
158 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
159 weak_factory_.GetWeakPtr(),
160 platform_path, file_info, file_ref, callback));
161 }
162
RunAfterPreWriteValidation(const base::FilePath & platform_path,const base::File::Info & file_info,const scoped_refptr<storage::ShareableFileReference> & file_ref,const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)163 void RunAfterPreWriteValidation(
164 const base::FilePath& platform_path,
165 const base::File::Info& file_info,
166 const scoped_refptr<storage::ShareableFileReference>& file_ref,
167 const CopyOrMoveOperationDelegate::StatusCallback& callback,
168 base::File::Error error) {
169 if (cancel_requested_)
170 error = base::File::FILE_ERROR_ABORT;
171
172 if (error != base::File::FILE_OK) {
173 callback.Run(error);
174 return;
175 }
176
177 // |file_ref| is unused but necessary to keep the file alive until
178 // CopyInForeignFile() is completed.
179 operation_runner_->CopyInForeignFile(
180 platform_path, dest_url_,
181 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
182 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
183 }
184
RunAfterCopyInForeignFile(const base::File::Info & file_info,const scoped_refptr<storage::ShareableFileReference> & file_ref,const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)185 void RunAfterCopyInForeignFile(
186 const base::File::Info& file_info,
187 const scoped_refptr<storage::ShareableFileReference>& file_ref,
188 const CopyOrMoveOperationDelegate::StatusCallback& callback,
189 base::File::Error error) {
190 if (cancel_requested_)
191 error = base::File::FILE_ERROR_ABORT;
192
193 if (error != base::File::FILE_OK) {
194 callback.Run(error);
195 return;
196 }
197
198 file_progress_callback_.Run(file_info.size);
199
200 if (option_ == FileSystemOperation::OPTION_NONE) {
201 RunAfterTouchFile(callback, base::File::FILE_OK);
202 return;
203 }
204
205 operation_runner_->TouchFile(
206 dest_url_, base::Time::Now() /* last_access */,
207 file_info.last_modified,
208 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
209 weak_factory_.GetWeakPtr(), callback));
210 }
211
RunAfterTouchFile(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)212 void RunAfterTouchFile(
213 const CopyOrMoveOperationDelegate::StatusCallback& callback,
214 base::File::Error error) {
215 // Even if TouchFile is failed, just ignore it.
216
217 if (cancel_requested_) {
218 callback.Run(base::File::FILE_ERROR_ABORT);
219 return;
220 }
221
222 // |validator_| is NULL when the destination filesystem does not do
223 // validation.
224 if (!validator_) {
225 // No validation is needed.
226 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
227 return;
228 }
229
230 PostWriteValidation(
231 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
232 weak_factory_.GetWeakPtr(), callback));
233 }
234
RunAfterPostWriteValidation(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)235 void RunAfterPostWriteValidation(
236 const CopyOrMoveOperationDelegate::StatusCallback& callback,
237 base::File::Error error) {
238 if (cancel_requested_) {
239 callback.Run(base::File::FILE_ERROR_ABORT);
240 return;
241 }
242
243 if (error != base::File::FILE_OK) {
244 // Failed to validate. Remove the destination file.
245 operation_runner_->Remove(
246 dest_url_, true /* recursive */,
247 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
248 weak_factory_.GetWeakPtr(), error, callback));
249 return;
250 }
251
252 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
253 callback.Run(base::File::FILE_OK);
254 return;
255 }
256
257 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
258
259 // Remove the source for finalizing move operation.
260 operation_runner_->Remove(
261 src_url_, true /* recursive */,
262 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
263 weak_factory_.GetWeakPtr(), callback));
264 }
265
RunAfterRemoveSourceForMove(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)266 void RunAfterRemoveSourceForMove(
267 const CopyOrMoveOperationDelegate::StatusCallback& callback,
268 base::File::Error error) {
269 if (cancel_requested_)
270 error = base::File::FILE_ERROR_ABORT;
271
272 if (error == base::File::FILE_ERROR_NOT_FOUND)
273 error = base::File::FILE_OK;
274 callback.Run(error);
275 }
276
DidRemoveDestForError(base::File::Error prior_error,const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)277 void DidRemoveDestForError(
278 base::File::Error prior_error,
279 const CopyOrMoveOperationDelegate::StatusCallback& callback,
280 base::File::Error error) {
281 if (error != base::File::FILE_OK) {
282 VLOG(1) << "Error removing destination file after validation error: "
283 << error;
284 }
285 callback.Run(prior_error);
286 }
287
288 // Runs pre-write validation.
PreWriteValidation(const base::FilePath & platform_path,const CopyOrMoveOperationDelegate::StatusCallback & callback)289 void PreWriteValidation(
290 const base::FilePath& platform_path,
291 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
292 DCHECK(validator_factory_);
293 validator_.reset(
294 validator_factory_->CreateCopyOrMoveFileValidator(
295 src_url_, platform_path));
296 validator_->StartPreWriteValidation(callback);
297 }
298
299 // Runs post-write validation.
PostWriteValidation(const CopyOrMoveOperationDelegate::StatusCallback & callback)300 void PostWriteValidation(
301 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
302 operation_runner_->CreateSnapshotFile(
303 dest_url_,
304 base::Bind(
305 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
306 weak_factory_.GetWeakPtr(), callback));
307 }
308
PostWriteValidationAfterCreateSnapshotFile(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error,const base::File::Info & file_info,const base::FilePath & platform_path,const scoped_refptr<storage::ShareableFileReference> & file_ref)309 void PostWriteValidationAfterCreateSnapshotFile(
310 const CopyOrMoveOperationDelegate::StatusCallback& callback,
311 base::File::Error error,
312 const base::File::Info& file_info,
313 const base::FilePath& platform_path,
314 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
315 if (cancel_requested_)
316 error = base::File::FILE_ERROR_ABORT;
317
318 if (error != base::File::FILE_OK) {
319 callback.Run(error);
320 return;
321 }
322
323 DCHECK(validator_);
324 // Note: file_ref passed here to keep the file alive until after
325 // the StartPostWriteValidation operation finishes.
326 validator_->StartPostWriteValidation(
327 platform_path,
328 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
329 weak_factory_.GetWeakPtr(), file_ref, callback));
330 }
331
332 // |file_ref| is unused; it is passed here to make sure the reference is
333 // alive until after post-write validation is complete.
DidPostWriteValidation(const scoped_refptr<storage::ShareableFileReference> & file_ref,const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)334 void DidPostWriteValidation(
335 const scoped_refptr<storage::ShareableFileReference>& file_ref,
336 const CopyOrMoveOperationDelegate::StatusCallback& callback,
337 base::File::Error error) {
338 callback.Run(error);
339 }
340
341 FileSystemOperationRunner* operation_runner_;
342 CopyOrMoveOperationDelegate::OperationType operation_type_;
343 FileSystemURL src_url_;
344 FileSystemURL dest_url_;
345
346 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
347 CopyOrMoveFileValidatorFactory* validator_factory_;
348 scoped_ptr<CopyOrMoveFileValidator> validator_;
349 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
350 bool cancel_requested_;
351 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
352 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
353 };
354
355 // The size of buffer for StreamCopyHelper.
356 const int kReadBufferSize = 32768;
357
358 // To avoid too many progress callbacks, it should be called less
359 // frequently than 50ms.
360 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
361
362 // Specifically for cross file system copy/move operation, this class uses
363 // stream reader and writer for copying. Validator is not supported, so if
364 // necessary SnapshotCopyOrMoveImpl should be used.
365 class StreamCopyOrMoveImpl
366 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
367 public:
StreamCopyOrMoveImpl(FileSystemOperationRunner * operation_runner,FileSystemContext * file_system_context,CopyOrMoveOperationDelegate::OperationType operation_type,const FileSystemURL & src_url,const FileSystemURL & dest_url,CopyOrMoveOperationDelegate::CopyOrMoveOption option,scoped_ptr<storage::FileStreamReader> reader,scoped_ptr<FileStreamWriter> writer,const FileSystemOperation::CopyFileProgressCallback & file_progress_callback)368 StreamCopyOrMoveImpl(
369 FileSystemOperationRunner* operation_runner,
370 FileSystemContext* file_system_context,
371 CopyOrMoveOperationDelegate::OperationType operation_type,
372 const FileSystemURL& src_url,
373 const FileSystemURL& dest_url,
374 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
375 scoped_ptr<storage::FileStreamReader> reader,
376 scoped_ptr<FileStreamWriter> writer,
377 const FileSystemOperation::CopyFileProgressCallback&
378 file_progress_callback)
379 : operation_runner_(operation_runner),
380 file_system_context_(file_system_context),
381 operation_type_(operation_type),
382 src_url_(src_url),
383 dest_url_(dest_url),
384 option_(option),
385 reader_(reader.Pass()),
386 writer_(writer.Pass()),
387 file_progress_callback_(file_progress_callback),
388 cancel_requested_(false),
389 weak_factory_(this) {}
390
Run(const CopyOrMoveOperationDelegate::StatusCallback & callback)391 virtual void Run(
392 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
393 // Reader can be created even if the entry does not exist or the entry is
394 // a directory. To check errors before destination file creation,
395 // check metadata first.
396 operation_runner_->GetMetadata(
397 src_url_,
398 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
399 weak_factory_.GetWeakPtr(), callback));
400 }
401
Cancel()402 virtual void Cancel() OVERRIDE {
403 cancel_requested_ = true;
404 if (copy_helper_)
405 copy_helper_->Cancel();
406 }
407
408 private:
NotifyOnStartUpdate(const FileSystemURL & url)409 void NotifyOnStartUpdate(const FileSystemURL& url) {
410 if (file_system_context_->GetUpdateObservers(url.type())) {
411 file_system_context_->GetUpdateObservers(url.type())
412 ->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
413 }
414 }
415
NotifyOnModifyFile(const FileSystemURL & url)416 void NotifyOnModifyFile(const FileSystemURL& url) {
417 if (file_system_context_->GetChangeObservers(url.type())) {
418 file_system_context_->GetChangeObservers(url.type())
419 ->Notify(&FileChangeObserver::OnModifyFile, MakeTuple(url));
420 }
421 }
422
NotifyOnEndUpdate(const FileSystemURL & url)423 void NotifyOnEndUpdate(const FileSystemURL& url) {
424 if (file_system_context_->GetUpdateObservers(url.type())) {
425 file_system_context_->GetUpdateObservers(url.type())
426 ->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
427 }
428 }
429
RunAfterGetMetadataForSource(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error,const base::File::Info & file_info)430 void RunAfterGetMetadataForSource(
431 const CopyOrMoveOperationDelegate::StatusCallback& callback,
432 base::File::Error error,
433 const base::File::Info& file_info) {
434 if (cancel_requested_)
435 error = base::File::FILE_ERROR_ABORT;
436
437 if (error != base::File::FILE_OK) {
438 callback.Run(error);
439 return;
440 }
441
442 if (file_info.is_directory) {
443 // If not a directory, failed with appropriate error code.
444 callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
445 return;
446 }
447
448 // To use FileStreamWriter, we need to ensure the destination file exists.
449 operation_runner_->CreateFile(
450 dest_url_,
451 true /* exclusive */,
452 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
453 weak_factory_.GetWeakPtr(),
454 callback,
455 file_info.last_modified));
456 }
457
RunAfterCreateFileForDestination(const CopyOrMoveOperationDelegate::StatusCallback & callback,const base::Time & last_modified,base::File::Error error)458 void RunAfterCreateFileForDestination(
459 const CopyOrMoveOperationDelegate::StatusCallback& callback,
460 const base::Time& last_modified,
461 base::File::Error error) {
462 if (cancel_requested_)
463 error = base::File::FILE_ERROR_ABORT;
464 // This conversion is to return the consistent status code with
465 // FileSystemFileUtil::Copy.
466 if (error == base::File::FILE_ERROR_NOT_A_FILE)
467 error = base::File::FILE_ERROR_INVALID_OPERATION;
468
469 if (error != base::File::FILE_OK &&
470 error != base::File::FILE_ERROR_EXISTS) {
471 callback.Run(error);
472 return;
473 }
474
475 if (error == base::File::FILE_ERROR_EXISTS) {
476 operation_runner_->Truncate(
477 dest_url_,
478 0 /* length */,
479 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
480 weak_factory_.GetWeakPtr(),
481 callback,
482 last_modified));
483 return;
484 }
485 RunAfterTruncateForDestination(
486 callback, last_modified, base::File::FILE_OK);
487 }
488
RunAfterTruncateForDestination(const CopyOrMoveOperationDelegate::StatusCallback & callback,const base::Time & last_modified,base::File::Error error)489 void RunAfterTruncateForDestination(
490 const CopyOrMoveOperationDelegate::StatusCallback& callback,
491 const base::Time& last_modified,
492 base::File::Error error) {
493 if (cancel_requested_)
494 error = base::File::FILE_ERROR_ABORT;
495
496 if (error != base::File::FILE_OK) {
497 callback.Run(error);
498 return;
499 }
500
501 const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
502 storage::COPY_SYNC_OPTION_SYNC;
503
504 NotifyOnStartUpdate(dest_url_);
505 DCHECK(!copy_helper_);
506 copy_helper_.reset(
507 new CopyOrMoveOperationDelegate::StreamCopyHelper(
508 reader_.Pass(), writer_.Pass(),
509 need_flush,
510 kReadBufferSize,
511 file_progress_callback_,
512 base::TimeDelta::FromMilliseconds(
513 kMinProgressCallbackInvocationSpanInMilliseconds)));
514 copy_helper_->Run(
515 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
516 weak_factory_.GetWeakPtr(), callback, last_modified));
517 }
518
RunAfterStreamCopy(const CopyOrMoveOperationDelegate::StatusCallback & callback,const base::Time & last_modified,base::File::Error error)519 void RunAfterStreamCopy(
520 const CopyOrMoveOperationDelegate::StatusCallback& callback,
521 const base::Time& last_modified,
522 base::File::Error error) {
523 NotifyOnModifyFile(dest_url_);
524 NotifyOnEndUpdate(dest_url_);
525 if (cancel_requested_)
526 error = base::File::FILE_ERROR_ABORT;
527
528 if (error != base::File::FILE_OK) {
529 callback.Run(error);
530 return;
531 }
532
533 if (option_ == FileSystemOperation::OPTION_NONE) {
534 RunAfterTouchFile(callback, base::File::FILE_OK);
535 return;
536 }
537
538 operation_runner_->TouchFile(
539 dest_url_, base::Time::Now() /* last_access */, last_modified,
540 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
541 weak_factory_.GetWeakPtr(), callback));
542 }
543
RunAfterTouchFile(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)544 void RunAfterTouchFile(
545 const CopyOrMoveOperationDelegate::StatusCallback& callback,
546 base::File::Error error) {
547 // Even if TouchFile is failed, just ignore it.
548 if (cancel_requested_) {
549 callback.Run(base::File::FILE_ERROR_ABORT);
550 return;
551 }
552
553 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
554 callback.Run(base::File::FILE_OK);
555 return;
556 }
557
558 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
559
560 // Remove the source for finalizing move operation.
561 operation_runner_->Remove(
562 src_url_, false /* recursive */,
563 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
564 weak_factory_.GetWeakPtr(), callback));
565 }
566
RunAfterRemoveForMove(const CopyOrMoveOperationDelegate::StatusCallback & callback,base::File::Error error)567 void RunAfterRemoveForMove(
568 const CopyOrMoveOperationDelegate::StatusCallback& callback,
569 base::File::Error error) {
570 if (cancel_requested_)
571 error = base::File::FILE_ERROR_ABORT;
572 if (error == base::File::FILE_ERROR_NOT_FOUND)
573 error = base::File::FILE_OK;
574 callback.Run(error);
575 }
576
577 FileSystemOperationRunner* operation_runner_;
578 scoped_refptr<FileSystemContext> file_system_context_;
579 CopyOrMoveOperationDelegate::OperationType operation_type_;
580 FileSystemURL src_url_;
581 FileSystemURL dest_url_;
582 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
583 scoped_ptr<storage::FileStreamReader> reader_;
584 scoped_ptr<FileStreamWriter> writer_;
585 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
586 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
587 bool cancel_requested_;
588 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
589 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
590 };
591
592 } // namespace
593
StreamCopyHelper(scoped_ptr<storage::FileStreamReader> reader,scoped_ptr<FileStreamWriter> writer,bool need_flush,int buffer_size,const FileSystemOperation::CopyFileProgressCallback & file_progress_callback,const base::TimeDelta & min_progress_callback_invocation_span)594 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
595 scoped_ptr<storage::FileStreamReader> reader,
596 scoped_ptr<FileStreamWriter> writer,
597 bool need_flush,
598 int buffer_size,
599 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
600 const base::TimeDelta& min_progress_callback_invocation_span)
601 : reader_(reader.Pass()),
602 writer_(writer.Pass()),
603 need_flush_(need_flush),
604 file_progress_callback_(file_progress_callback),
605 io_buffer_(new net::IOBufferWithSize(buffer_size)),
606 num_copied_bytes_(0),
607 previous_flush_offset_(0),
608 min_progress_callback_invocation_span_(
609 min_progress_callback_invocation_span),
610 cancel_requested_(false),
611 weak_factory_(this) {
612 }
613
~StreamCopyHelper()614 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
615 }
616
Run(const StatusCallback & callback)617 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
618 const StatusCallback& callback) {
619 file_progress_callback_.Run(0);
620 last_progress_callback_invocation_time_ = base::Time::Now();
621 Read(callback);
622 }
623
Cancel()624 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
625 cancel_requested_ = true;
626 }
627
Read(const StatusCallback & callback)628 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
629 const StatusCallback& callback) {
630 int result = reader_->Read(
631 io_buffer_.get(), io_buffer_->size(),
632 base::Bind(&StreamCopyHelper::DidRead,
633 weak_factory_.GetWeakPtr(), callback));
634 if (result != net::ERR_IO_PENDING)
635 DidRead(callback, result);
636 }
637
DidRead(const StatusCallback & callback,int result)638 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
639 const StatusCallback& callback, int result) {
640 if (cancel_requested_) {
641 callback.Run(base::File::FILE_ERROR_ABORT);
642 return;
643 }
644
645 if (result < 0) {
646 callback.Run(NetErrorToFileError(result));
647 return;
648 }
649
650 if (result == 0) {
651 // Here is the EOF.
652 if (need_flush_)
653 Flush(callback, true /* is_eof */);
654 else
655 callback.Run(base::File::FILE_OK);
656 return;
657 }
658
659 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
660 }
661
Write(const StatusCallback & callback,scoped_refptr<net::DrainableIOBuffer> buffer)662 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
663 const StatusCallback& callback,
664 scoped_refptr<net::DrainableIOBuffer> buffer) {
665 DCHECK_GT(buffer->BytesRemaining(), 0);
666
667 int result = writer_->Write(
668 buffer.get(), buffer->BytesRemaining(),
669 base::Bind(&StreamCopyHelper::DidWrite,
670 weak_factory_.GetWeakPtr(), callback, buffer));
671 if (result != net::ERR_IO_PENDING)
672 DidWrite(callback, buffer, result);
673 }
674
DidWrite(const StatusCallback & callback,scoped_refptr<net::DrainableIOBuffer> buffer,int result)675 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
676 const StatusCallback& callback,
677 scoped_refptr<net::DrainableIOBuffer> buffer,
678 int result) {
679 if (cancel_requested_) {
680 callback.Run(base::File::FILE_ERROR_ABORT);
681 return;
682 }
683
684 if (result < 0) {
685 callback.Run(NetErrorToFileError(result));
686 return;
687 }
688
689 buffer->DidConsume(result);
690 num_copied_bytes_ += result;
691
692 // Check the elapsed time since last |file_progress_callback_| invocation.
693 base::Time now = base::Time::Now();
694 if (now - last_progress_callback_invocation_time_ >=
695 min_progress_callback_invocation_span_) {
696 file_progress_callback_.Run(num_copied_bytes_);
697 last_progress_callback_invocation_time_ = now;
698 }
699
700 if (buffer->BytesRemaining() > 0) {
701 Write(callback, buffer);
702 return;
703 }
704
705 if (need_flush_ &&
706 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
707 Flush(callback, false /* not is_eof */);
708 } else {
709 Read(callback);
710 }
711 }
712
Flush(const StatusCallback & callback,bool is_eof)713 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
714 const StatusCallback& callback, bool is_eof) {
715 int result = writer_->Flush(
716 base::Bind(&StreamCopyHelper::DidFlush,
717 weak_factory_.GetWeakPtr(), callback, is_eof));
718 if (result != net::ERR_IO_PENDING)
719 DidFlush(callback, is_eof, result);
720 }
721
DidFlush(const StatusCallback & callback,bool is_eof,int result)722 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
723 const StatusCallback& callback, bool is_eof, int result) {
724 if (cancel_requested_) {
725 callback.Run(base::File::FILE_ERROR_ABORT);
726 return;
727 }
728
729 previous_flush_offset_ = num_copied_bytes_;
730 if (is_eof)
731 callback.Run(NetErrorToFileError(result));
732 else
733 Read(callback);
734 }
735
CopyOrMoveOperationDelegate(FileSystemContext * file_system_context,const FileSystemURL & src_root,const FileSystemURL & dest_root,OperationType operation_type,CopyOrMoveOption option,const CopyProgressCallback & progress_callback,const StatusCallback & callback)736 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
737 FileSystemContext* file_system_context,
738 const FileSystemURL& src_root,
739 const FileSystemURL& dest_root,
740 OperationType operation_type,
741 CopyOrMoveOption option,
742 const CopyProgressCallback& progress_callback,
743 const StatusCallback& callback)
744 : RecursiveOperationDelegate(file_system_context),
745 src_root_(src_root),
746 dest_root_(dest_root),
747 operation_type_(operation_type),
748 option_(option),
749 progress_callback_(progress_callback),
750 callback_(callback),
751 weak_factory_(this) {
752 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
753 }
754
~CopyOrMoveOperationDelegate()755 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
756 STLDeleteElements(&running_copy_set_);
757 }
758
Run()759 void CopyOrMoveOperationDelegate::Run() {
760 // Not supported; this should never be called.
761 NOTREACHED();
762 }
763
RunRecursively()764 void CopyOrMoveOperationDelegate::RunRecursively() {
765 // Perform light-weight checks first.
766
767 // It is an error to try to copy/move an entry into its child.
768 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
769 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
770 return;
771 }
772
773 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
774 // In JS API this should return error, but we return success because Pepper
775 // wants to return success and we have a code path that returns error in
776 // Blink for JS (http://crbug.com/329517).
777 callback_.Run(base::File::FILE_OK);
778 return;
779 }
780
781 // Start to process the source directory recursively.
782 // TODO(kinuko): This could be too expensive for same_file_system_==true
783 // and operation==MOVE case, probably we can just rename the root directory.
784 // http://crbug.com/172187
785 StartRecursiveOperation(src_root_, callback_);
786 }
787
ProcessFile(const FileSystemURL & src_url,const StatusCallback & callback)788 void CopyOrMoveOperationDelegate::ProcessFile(
789 const FileSystemURL& src_url,
790 const StatusCallback& callback) {
791 if (!progress_callback_.is_null()) {
792 progress_callback_.Run(
793 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
794 }
795
796 FileSystemURL dest_url = CreateDestURL(src_url);
797 CopyOrMoveImpl* impl = NULL;
798 if (same_file_system_ &&
799 (file_system_context()
800 ->GetFileSystemBackend(src_url.type())
801 ->HasInplaceCopyImplementation(src_url.type()) ||
802 operation_type_ == OPERATION_MOVE)) {
803 impl = new CopyOrMoveOnSameFileSystemImpl(
804 operation_runner(), operation_type_, src_url, dest_url, option_,
805 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
806 weak_factory_.GetWeakPtr(), src_url));
807 } else {
808 // Cross filesystem case.
809 base::File::Error error = base::File::FILE_ERROR_FAILED;
810 CopyOrMoveFileValidatorFactory* validator_factory =
811 file_system_context()->GetCopyOrMoveFileValidatorFactory(
812 dest_root_.type(), &error);
813 if (error != base::File::FILE_OK) {
814 callback.Run(error);
815 return;
816 }
817
818 if (!validator_factory) {
819 scoped_ptr<storage::FileStreamReader> reader =
820 file_system_context()->CreateFileStreamReader(
821 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
822 scoped_ptr<FileStreamWriter> writer =
823 file_system_context()->CreateFileStreamWriter(dest_url, 0);
824 if (reader && writer) {
825 impl = new StreamCopyOrMoveImpl(
826 operation_runner(),
827 file_system_context(),
828 operation_type_,
829 src_url,
830 dest_url,
831 option_,
832 reader.Pass(),
833 writer.Pass(),
834 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
835 weak_factory_.GetWeakPtr(),
836 src_url));
837 }
838 }
839
840 if (!impl) {
841 impl = new SnapshotCopyOrMoveImpl(
842 operation_runner(), operation_type_, src_url, dest_url, option_,
843 validator_factory,
844 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
845 weak_factory_.GetWeakPtr(), src_url));
846 }
847 }
848
849 // Register the running task.
850 running_copy_set_.insert(impl);
851 impl->Run(base::Bind(
852 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
853 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
854 }
855
ProcessDirectory(const FileSystemURL & src_url,const StatusCallback & callback)856 void CopyOrMoveOperationDelegate::ProcessDirectory(
857 const FileSystemURL& src_url,
858 const StatusCallback& callback) {
859 if (src_url == src_root_) {
860 // The src_root_ looks to be a directory.
861 // Try removing the dest_root_ to see if it exists and/or it is an
862 // empty directory.
863 // We do not invoke |progress_callback_| for source root, because it is
864 // already called in ProcessFile().
865 operation_runner()->RemoveDirectory(
866 dest_root_,
867 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
868 weak_factory_.GetWeakPtr(), callback));
869 return;
870 }
871
872 if (!progress_callback_.is_null()) {
873 progress_callback_.Run(
874 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
875 }
876
877 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
878 }
879
PostProcessDirectory(const FileSystemURL & src_url,const StatusCallback & callback)880 void CopyOrMoveOperationDelegate::PostProcessDirectory(
881 const FileSystemURL& src_url,
882 const StatusCallback& callback) {
883 if (option_ == FileSystemOperation::OPTION_NONE) {
884 PostProcessDirectoryAfterTouchFile(
885 src_url, callback, base::File::FILE_OK);
886 return;
887 }
888
889 operation_runner()->GetMetadata(
890 src_url,
891 base::Bind(
892 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
893 weak_factory_.GetWeakPtr(), src_url, callback));
894 }
895
OnCancel()896 void CopyOrMoveOperationDelegate::OnCancel() {
897 // Request to cancel all running Copy/Move file.
898 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
899 iter != running_copy_set_.end(); ++iter)
900 (*iter)->Cancel();
901 }
902
DidCopyOrMoveFile(const FileSystemURL & src_url,const FileSystemURL & dest_url,const StatusCallback & callback,CopyOrMoveImpl * impl,base::File::Error error)903 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
904 const FileSystemURL& src_url,
905 const FileSystemURL& dest_url,
906 const StatusCallback& callback,
907 CopyOrMoveImpl* impl,
908 base::File::Error error) {
909 running_copy_set_.erase(impl);
910 delete impl;
911
912 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
913 progress_callback_.Run(
914 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
915 }
916
917 callback.Run(error);
918 }
919
DidTryRemoveDestRoot(const StatusCallback & callback,base::File::Error error)920 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
921 const StatusCallback& callback,
922 base::File::Error error) {
923 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
924 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
925 return;
926 }
927 if (error != base::File::FILE_OK &&
928 error != base::File::FILE_ERROR_NOT_FOUND) {
929 callback_.Run(error);
930 return;
931 }
932
933 ProcessDirectoryInternal(src_root_, dest_root_, callback);
934 }
935
ProcessDirectoryInternal(const FileSystemURL & src_url,const FileSystemURL & dest_url,const StatusCallback & callback)936 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
937 const FileSystemURL& src_url,
938 const FileSystemURL& dest_url,
939 const StatusCallback& callback) {
940 // If operation_type == Move we may need to record directories and
941 // restore directory timestamps in the end, though it may have
942 // negative performance impact.
943 // See http://crbug.com/171284 for more details.
944 operation_runner()->CreateDirectory(
945 dest_url, false /* exclusive */, false /* recursive */,
946 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
947 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
948 }
949
DidCreateDirectory(const FileSystemURL & src_url,const FileSystemURL & dest_url,const StatusCallback & callback,base::File::Error error)950 void CopyOrMoveOperationDelegate::DidCreateDirectory(
951 const FileSystemURL& src_url,
952 const FileSystemURL& dest_url,
953 const StatusCallback& callback,
954 base::File::Error error) {
955 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
956 progress_callback_.Run(
957 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
958 }
959
960 callback.Run(error);
961 }
962
PostProcessDirectoryAfterGetMetadata(const FileSystemURL & src_url,const StatusCallback & callback,base::File::Error error,const base::File::Info & file_info)963 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
964 const FileSystemURL& src_url,
965 const StatusCallback& callback,
966 base::File::Error error,
967 const base::File::Info& file_info) {
968 if (error != base::File::FILE_OK) {
969 // Ignore the error, and run post process which should run after TouchFile.
970 PostProcessDirectoryAfterTouchFile(
971 src_url, callback, base::File::FILE_OK);
972 return;
973 }
974
975 operation_runner()->TouchFile(
976 CreateDestURL(src_url), base::Time::Now() /* last access */,
977 file_info.last_modified,
978 base::Bind(
979 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
980 weak_factory_.GetWeakPtr(), src_url, callback));
981 }
982
PostProcessDirectoryAfterTouchFile(const FileSystemURL & src_url,const StatusCallback & callback,base::File::Error error)983 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
984 const FileSystemURL& src_url,
985 const StatusCallback& callback,
986 base::File::Error error) {
987 // Even if the TouchFile is failed, just ignore it.
988
989 if (operation_type_ == OPERATION_COPY) {
990 callback.Run(base::File::FILE_OK);
991 return;
992 }
993
994 DCHECK_EQ(OPERATION_MOVE, operation_type_);
995
996 // All files and subdirectories in the directory should be moved here,
997 // so remove the source directory for finalizing move operation.
998 operation_runner()->Remove(
999 src_url, false /* recursive */,
1000 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
1001 weak_factory_.GetWeakPtr(), callback));
1002 }
1003
DidRemoveSourceForMove(const StatusCallback & callback,base::File::Error error)1004 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1005 const StatusCallback& callback,
1006 base::File::Error error) {
1007 if (error == base::File::FILE_ERROR_NOT_FOUND)
1008 error = base::File::FILE_OK;
1009 callback.Run(error);
1010 }
1011
OnCopyFileProgress(const FileSystemURL & src_url,int64 size)1012 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1013 const FileSystemURL& src_url, int64 size) {
1014 if (!progress_callback_.is_null()) {
1015 progress_callback_.Run(
1016 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1017 }
1018 }
1019
CreateDestURL(const FileSystemURL & src_url) const1020 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1021 const FileSystemURL& src_url) const {
1022 DCHECK_EQ(src_root_.type(), src_url.type());
1023 DCHECK_EQ(src_root_.origin(), src_url.origin());
1024
1025 base::FilePath relative = dest_root_.virtual_path();
1026 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1027 &relative);
1028 return file_system_context()->CreateCrackedFileSystemURL(
1029 dest_root_.origin(),
1030 dest_root_.mount_type(),
1031 relative);
1032 }
1033
1034 } // namespace storage
1035